Analyzing and Forecasting Tree Canopy Loss in the City of Melbourne
Authored by: Samiha Haque
Duration: 90 mins
Level: Intermediate
Pre-requisite Skills: Python, GeoDataFrames, Regression models, Neural Networks, Weighted least squares, Cook's distance, Folium Maps.
Scenario

As a governor of the City of Melbourne, I want to analyze and predict changes in tree canopy, so that I can forecast future canopy loss over the next two years if no new trees are planted. By comparing these predictions with the Urban Forest Strategy's tree planting zone schedules, I aim to assess and enhance the effectiveness of the planting schedules, ensuring sustainable urban forestry and improving the city's green spaces.

What this use case will teach you

At the end of this use case you will:

  • Learn to work with GeoDataFrames, geometries and geo shapes.
  • Learn pre-processing techniques such as fixing skewness, standardizing and normalizing.
  • Learn to apply Regression models: Linear Regression, Random Forest Model, Support Vector Regression, Gradient Boosting Regression, RANSAC, Huber Regression, Polynomial Regression.
  • Learn to analyze residual plots.
  • Learn to build basic neural network model.
  • Learn to use Weighted Least Squares method
  • Learn to use Cook's distance to identify and filter out influential points
  • Learn to plot multiple geolocations on a map using folium.
  • Learn to display multiple maps on different tabs using folium.
Urban Canopy Preservation: Leveraging Predictive Modeling to Support Melbourne's Tree Planting Strategy and SDG 'Life on Land'

One of the 16 Sustainable Development Goals (SDGs) is "Life on Land," which aims to "protect, restore, and promote the sustainable use of terrestrial ecosystems, sustainably manage forests, combat desertification, halt and reverse land degradation, and stop biodiversity loss." In alignment with this goal, the City of Melbourne seeks to promote sustainable forest management through urban greening and afforestation. This use case focuses on identifying areas where tree canopies have been lost over time to support this objective.

The primary goal of the analysis was to detect locations experiencing tree canopy loss. Data from 2018 and 2019 was used to predict canopy loss in 2021, and subsequently, data from 2019 and 2021 was utilized to forecast potential loss in 2023. Since data for 2023 is not available, I built the model using 2021 values, assuming similar trends or patterns would continue. The emphasis was on identifying areas with a decline in canopy coverage to assess which locations would continue to lose tree cover if no new trees were planted. I then cross-referenced this information with Melbourne's tree planting zones and schedules to identify areas that the City of Melbourne may be overlooking but where tree planting is crucial due to canopy decline.

Initially, I experimented with several regression techniques, including Linear Regression, Random Forest Model, Support Vector Regression, Gradient Boosting Regression, RANSAC, Huber Regression, and Polynomial Regression. However, all these models displayed funnel-shaped residual plots, indicating heteroscedasticity. I then turned to a neural network model, but it produced similar results. Eventually, I opted for a weighted least squares (WLS) method and applied Cook's distance to remove influential points, which improved the model's performance. This final model was then used to predict tree canopy loss in 2023.

Datasets used
  • [Tree Canopies 2016 (Urban Forest)

](https://data.melbourne.vic.gov.au/explore/dataset/tree-canopies-2016-urban-forest/information/)
The 2016 tree canopy data, mapped using aerial photos and LiDAR, served as a baseline to compare canopy changes over time, helping to identify areas with significant canopy loss. It mainly contains geopoint, geoshape and area. This dataset is imported from Melbourne Open Data website, using API V2.1.

  • [Tree canopies 2018 entire municipal area (Urban Forest)

](https://data.melbourne.vic.gov.au/explore/dataset/tree-canopies-2018-entire-municipal-area-urban-forest/information/)
This dataset contains the geopoint, geoshape, objectid, shape length and shape area. This dataset is imported from Melbourne Open Data website, using API V2.1.

  • [Tree canopies 2019)

](https://data.melbourne.vic.gov.au/explore/dataset/tree-canopies-2019/information/)
This dataset contains the geopoint, geoshape and id. The 2019 dataset contains spatial polygons representing tree canopy areas across the City of Melbourne, which can be mapped using the geometry column. It was used to track canopy changes by comparing it with other years and to predict future canopy loss trends. This dataset is imported from Melbourne Open Data website, using API V2.1.

  • Tree Canopies 2021 (Urban Forest))

This dataset contains the geopoint and geoshape. The 2021 dataset maps tree canopy within the City of Melbourne using high-resolution multi-spectral imagery. The canopy polygons represent actual tree canopy extents on both private and public properties across the city. It was used to assess recent changes in tree canopy and forecast future loss by comparing it with previous years, such as 2018 and 2019. This dataset is imported from Melbourne Open Data website, using API V2.1.

  • [Tree planting zone schedules, with years (Urban Forest)

](https://data.melbourne.vic.gov.au/explore/dataset/tree-planting-zone-schedules-with-years-urban-forest/information/)
This dataset mainly contains the geopoint, geoshape and planting schedules. This dataset outlines the anticipated tree planting schedules across the City of Melbourne, providing a provisional timeframe for planting in each street. It was used to compare areas of tree canopy loss with scheduled planting zones, identifying locations that may need priority due to a decrease in canopy coverage, even if they are currently overlooked in the plan. This dataset is imported from Melbourne Open Data website, using API V2.1.

In [183]:
import requests
import pandas as pd
import numpy as np
from io import StringIO
import seaborn as sns
import matplotlib.pyplot as plt
import geopandas as gpd
from shapely.geometry import shape
import folium
from IPython.display import display, HTML
from geopy.geocoders import Nominatim
from sklearn.preprocessing import PowerTransformer
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.linear_model import HuberRegressor
from sklearn.linear_model import RANSACRegressor
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import make_scorer, mean_absolute_error, r2_score,mean_squared_error
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense 
from tensorflow.keras.optimizers import Adadelta
from keras.callbacks import EarlyStopping
import tensorflow.keras.backend as K
import statsmodels.api as sm
In [184]:
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='tree-canopies-2016-urban-forest'


url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}

response=requests.get(url,params=params)

if response.status_code==200:
    url_content=response.content.decode('utf-8')
    canopy_2016=pd.read_csv(StringIO(url_content),delimiter=';')
else:
    print(f'Request failed with status code {response.status_code}')
In [185]:
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='tree-canopies-2018-entire-municipal-area-urban-forest'


url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}

response=requests.get(url,params=params)

if response.status_code==200:
    url_content=response.content.decode('utf-8')
    canopy_2018=pd.read_csv(StringIO(url_content),delimiter=';')
    print(canopy_2018.head(10))

else:
    print(f'Request failed with status code {response.status_code}')
                              geo_point_2d  \
0   -37.84602285495841, 144.97924782873844   
1   -37.845962192617044, 144.9799793892225   
2   -37.84588630574396, 144.98021376931501   
3  -37.845852004062884, 144.98487772492962   
4    -37.84319818316156, 144.9031046911688   
5   -37.84573243353317, 144.98060993453439   
6  -37.845787857069794, 144.98469142233296   
7  -37.845767803351194, 144.98456044006682   
8   -37.845759265667134, 144.9847978689059   
9  -37.845659321213056, 144.97986343165596   

                                           geo_shape  objectid  shape_leng  \
0  {"coordinates": [[[[144.97925092251597, -37.84...       485   46.817501   
1  {"coordinates": [[[[144.98008850396891, -37.84...       491   59.992940   
2  {"coordinates": [[[[144.98022149936796, -37.84...       493    6.815275   
3  {"coordinates": [[[[144.98488143894568, -37.84...       501    9.021635   
4  {"coordinates": [[[[144.9031086928607, -37.843...       660    6.921479   
5  {"coordinates": [[[[144.9806147500726, -37.845...       503    7.458243   
6  {"coordinates": [[[[144.98465008735135, -37.84...       505   45.870887   
7  {"coordinates": [[[[144.984587556976, -37.8457...       508   17.194188   
8  {"coordinates": [[[[144.98481269456255, -37.84...       509    8.247115   
9  {"coordinates": [[[[144.97986991352417, -37.84...       512    6.939052   

   shape_area  
0  104.207432  
1   97.591279  
2    3.219528  
3    5.402039  
4    3.428334  
5    4.221254  
6   82.573147  
7   20.453148  
8    5.186877  
9    3.276969  
In [186]:
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='tree-canopies-2019'


url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}

response=requests.get(url,params=params)

if response.status_code==200:
    url_content=response.content.decode('utf-8')
    canopy_2019=pd.read_csv(StringIO(url_content),delimiter=';')
    print(canopy_2019.head(10))
else:
    print(f'Request failed with status code {response.status_code}')
                              geo_point_2d  \
0  -37.806939661373356, 144.94525884712138   
1   -37.80696249351158, 144.94871645633398   
2   -37.80695305356777, 144.94666702574662   
3   -37.80686661161844, 144.94324549515883   
4   -37.80676649515245, 144.93740058973637   
5   -37.80728115533656, 144.96583090095476   
6    -37.80677399539525, 144.9368268115774   
7     -37.8072147454235, 144.9623418560369   
8   -37.80736894582776, 144.97144239399995   
9  -37.807246670404545, 144.96473046094926   

                                           geo_shape     id  
0  {"coordinates": [[[144.945262941, -37.80694305...  60528  
1  {"coordinates": [[[144.948703227, -37.80698951...  60513  
2  {"coordinates": [[[144.9466682286, -37.8069540...  60512  
3  {"coordinates": [[[144.943239676, -37.80689421...  60511  
4  {"coordinates": [[[144.9374100872, -37.8067899...  60510  
5  {"coordinates": [[[144.9658321041, -37.8072821...  60508  
6  {"coordinates": [[[144.9368256089, -37.8067729...  60503  
7  {"coordinates": [[[144.9623440158, -37.8072173...  60500  
8  {"coordinates": [[[144.9714435972, -37.8073699...  60496  
9  {"coordinates": [[[144.9647340703, -37.8072496...  60486  
In [187]:
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='tree-canopies-2021-urban-forest'


url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}

response=requests.get(url,params=params)

if response.status_code==200:
    url_content=response.content.decode('utf-8')
    canopy_2021=pd.read_csv(StringIO(url_content),delimiter=';')
    print(canopy_2021.head(10))
else:
    print(f'Request failed with status code {response.status_code}')
                              geo_point_2d  \
0   -37.77506304683423, 144.93898465421296   
1  -37.775132956993566, 144.93979253397976   
2   -37.77515941768105, 144.93986878938023   
3   -37.775130649990984, 144.9389679196713   
4   -37.77518700935825, 144.93875479828375   
5    -37.77519588721749, 144.9400498374677   
6     -37.77523198796972, 144.939139783017   
7     -37.7877402517759, 144.9630187754576   
8   -37.77531133181187, 144.93995080107067   
9   -37.78099284465387, 144.91549621280674   

                                           geo_shape  
0  {"coordinates": [[[[144.9389624164712, -37.775...  
1  {"coordinates": [[[[144.93978541786475, -37.77...  
2  {"coordinates": [[[[144.93986368038625, -37.77...  
3  {"coordinates": [[[[144.93894119058956, -37.77...  
4  {"coordinates": [[[[144.93877858274755, -37.77...  
5  {"coordinates": [[[[144.94001353302923, -37.77...  
6  {"coordinates": [[[[144.93913191868984, -37.77...  
7  {"coordinates": [[[[144.96296951252782, -37.78...  
8  {"coordinates": [[[[144.93994420827104, -37.77...  
9  {"coordinates": [[[[144.91549518501083, -37.78...  
In [188]:
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='tree-planting-zone-schedules-with-years-urban-forest'


url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}

response=requests.get(url,params=params)

if response.status_code==200:
    url_content=response.content.decode('utf-8')
    zones=pd.read_csv(StringIO(url_content),delimiter=';')
    print(zones.head(10))
else:
    print(f'Request failed with status code {response.status_code}')
                              geo_point_2d  \
0    -37.8030612625994, 144.96879535330316   
1    -37.81979235869452, 144.9675112730786   
2  -37.796602429685905, 144.96990674715127   
3    -37.79774465832566, 144.9502620746376   
4    -37.81652483951976, 144.9864951838192   
5   -37.80052529463123, 144.97301577291447   
6   -37.80320423406633, 144.96274342342963   
7   -37.80458396557453, 144.95387035874043   
8    -37.80577394795748, 144.9645540416368   
9    -37.79087369913382, 144.9301190541952   

                                           geo_shape  str_from  segpart  \
0  {"coordinates": [[[[144.969387175266, -37.8029...       NaN      NaN   
1  {"coordinates": [[[[144.96828098035, -37.81969...       NaN      NaN   
2  {"coordinates": [[[[144.969341164027, -37.7965...       NaN      NaN   
3  {"coordinates": [[[[144.950283591209, -37.7975...       NaN      NaN   
4  {"coordinates": [[[[144.986101797552, -37.8163...       NaN      NaN   
5  {"coordinates": [[[[144.97346041968, -37.80048...       NaN      NaN   
6  {"coordinates": [[[[144.963299976829, -37.8033...       NaN      NaN   
7  {"coordinates": [[[[144.953636628275, -37.8049...       NaN      NaN   
8  {"coordinates": [[[[144.964686905857, -37.8060...       NaN      NaN   
9  {"coordinates": [[[[144.930311886162, -37.7911...       NaN      NaN   

   statusid  segid  streetname  streetid                         schedule  \
0       NaN  21556         NaN       NaN  Not determined by precinct plan   
1       NaN  22067         NaN       NaN                     Years 8 - 10   
2       NaN  20697         NaN       NaN                      Years 5 - 7   
3       NaN  21195         NaN       NaN                         Complete   
4       NaN  21945         NaN       NaN  Not determined by precinct plan   
5       NaN  20603         NaN       NaN  Not determined by precinct plan   
6       NaN  20838         NaN       NaN  Not determined by precinct plan   
7       NaN  20956         NaN       NaN                         Complete   
8       NaN  20504         NaN       NaN                     Years 8 - 10   
9       NaN  22997         NaN       NaN                     Years 8 - 10   

   mapstatus  str_to                                           segdescr  
0        NaN     NaN  Pelham Street between Rathdowne Street and Dru...  
1        NaN     NaN  Riverside Avenue between St Kilda Road and Sou...  
2        NaN     NaN  Little Palmerston Street between Rathdowne Str...  
3        NaN     NaN  Chapman Street between Errol Street and Harker...  
4        NaN     NaN  Wellington Parade between Simpson Street and P...  
5        NaN     NaN  Owen Street between Barrup Street and Canning ...  
6        NaN     NaN  Lincoln Square South between Swanston Street a...  
7        NaN     NaN  Howard Street between Victoria Street and Quee...  
8        NaN     NaN  Cardigan Street between Victoria Street and Qu...  
9        NaN     NaN  Eastwood Street between Mulgrave Street and Sm...  
In [189]:
canopy_2016.isna().sum()
Out[189]:
geo_point_2d    0
geo_shape       0
area            0
dtype: int64
In [190]:
canopy_2018.isna().sum()
Out[190]:
geo_point_2d    0
geo_shape       0
objectid        0
shape_leng      0
shape_area      0
dtype: int64
In [191]:
canopy_2021.head()
Out[191]:
geo_point_2d geo_shape
0 -37.77506304683423, 144.93898465421296 {"coordinates": [[[[144.9389624164712, -37.775...
1 -37.775132956993566, 144.93979253397976 {"coordinates": [[[[144.93978541786475, -37.77...
2 -37.77515941768105, 144.93986878938023 {"coordinates": [[[[144.93986368038625, -37.77...
3 -37.775130649990984, 144.9389679196713 {"coordinates": [[[[144.93894119058956, -37.77...
4 -37.77518700935825, 144.93875479828375 {"coordinates": [[[[144.93877858274755, -37.77...
In [192]:
canopy_2021.shape
Out[192]:
(57980, 2)
In [193]:
canopy_2021.isna().sum()
Out[193]:
geo_point_2d    0
geo_shape       0
dtype: int64
In [194]:
canopy_2019.head()
Out[194]:
geo_point_2d geo_shape id
0 -37.806939661373356, 144.94525884712138 {"coordinates": [[[144.945262941, -37.80694305... 60528
1 -37.80696249351158, 144.94871645633398 {"coordinates": [[[144.948703227, -37.80698951... 60513
2 -37.80695305356777, 144.94666702574662 {"coordinates": [[[144.9466682286, -37.8069540... 60512
3 -37.80686661161844, 144.94324549515883 {"coordinates": [[[144.943239676, -37.80689421... 60511
4 -37.80676649515245, 144.93740058973637 {"coordinates": [[[144.9374100872, -37.8067899... 60510
In [195]:
canopy_2019.shape
Out[195]:
(114784, 3)
In [196]:
canopy_2019.isna().sum()
Out[196]:
geo_point_2d    0
geo_shape       0
id              0
dtype: int64

Calculating the geometry and setting up the crs¶

This section processes geospatial data representing tree canopy areas for the years 2016, 2018, 2019, and 2021. Initially, the code extracts geometric shapes from the data and creates GeoDataFrames for each year, ensuring that they are correctly set to the WGS84 coordinate reference system (EPSG:4326). The geometries are then transformed to the EPSG:28355 coordinate system to maintain consistency in spatial analysis. Subsequently, the area of each canopy geometry is calculated for each year. This approach is crucial as it allows for accurate comparisons of tree canopy changes over time, helping to understand trends in urban forestry and the impact of environmental policies.

In [197]:
canopy_2016['geometry_2016'] = canopy_2016['geo_shape'].apply(lambda x: shape(eval(x)))
In [198]:
gdf_2016 = gpd.GeoDataFrame(canopy_2016, geometry='geometry_2016')
gdf_2016.crs = 'EPSG:4326'
In [199]:
canopy_2018['geometry_2018'] = canopy_2018['geo_shape'].apply(lambda x: shape(eval(x)))
In [200]:
gdf_2018 = gpd.GeoDataFrame(canopy_2018, geometry='geometry_2018')
In [201]:
gdf_2018.crs = 'EPSG:4326'
In [202]:
canopy_2019['geometry_2019'] = canopy_2019['geo_shape'].apply(lambda x: shape(eval(x)))
canopy_2021['geometry_2021'] = canopy_2021['geo_shape'].apply(lambda x: shape(eval(x)))
In [203]:
gdf_2019 = gpd.GeoDataFrame(canopy_2019, geometry='geometry_2019')
gdf_2021 = gpd.GeoDataFrame(canopy_2021, geometry='geometry_2021')
In [204]:
gdf_2019.crs = 'EPSG:4326'
gdf_2021.crs = 'EPSG:4326'
In [205]:
gdf_2016['geometry_2016'] = gdf_2016['geometry_2016'].to_crs('EPSG:28355')
In [206]:
gdf_2018['geometry_2018'] = gdf_2018['geometry_2018'].to_crs('EPSG:28355')
gdf_2019['geometry_2019'] = gdf_2019['geometry_2019'].to_crs('EPSG:28355')
gdf_2021['geometry_2021'] = gdf_2021['geometry_2021'].to_crs('EPSG:28355')
In [207]:
gdf_2016['area_2016'] = gdf_2016.geometry.area
In [208]:
gdf_2018['area_2018'] = gdf_2018.geometry.area
gdf_2019['area_2019'] = gdf_2019.geometry.area
gdf_2021['area_2021'] = gdf_2021.geometry.area

Creating the intersecting dataset¶

This section computes the intersections of the canopy geometries from the different years using 'overlay' function to identify common areas. This process helps in understanding how specific regions of the tree canopy have changed over time.

In [209]:
overlap_2018_2019 = gpd.overlay(gdf_2018, gdf_2019, how='intersection')
In [210]:
overlap_2018_2019_2021 = gpd.overlay(overlap_2018_2019, gdf_2021, how='intersection')
In [211]:
gdf_2016.columns=['geo_point_2016','geo_shape_2016','area','geometry_2016','area_2016']
In [212]:
overlap_2018_2019_2021_2016=gpd.overlay(overlap_2018_2019_2021, gdf_2016, how='intersection')
In [213]:
overlap_2018_2019_2021_2016.columns
Out[213]:
Index(['geo_point_2d_1', 'geo_shape_1', 'objectid', 'shape_leng', 'shape_area',
       'area_2018', 'geo_point_2d_2', 'geo_shape_2', 'id', 'area_2019',
       'geo_point_2d', 'geo_shape', 'area_2021', 'geo_point_2016',
       'geo_shape_2016', 'area', 'area_2016', 'geometry'],
      dtype='object')

In the code, a dictionary called main_dict is created to store important information, including geographical points and area measurements for each overlapping section of tree canopy from 2018, 2019, 2021, and 2016. During the iteration over the intersections of the geometries, the code checks specific conditions: it ensures that the total area of the canopy in 2016 is greater than that in 2018, and that the areas continue to decrease from 2018 to 2021. This is crucial because the overlap function can produce fragmented geometries with the same ID, leading to potential discrepancies in area calculations. By summing the areas of these fragmented sections, the code verifies that the overall area associated with each ID actually reflects a decreasing trend over time. If the conditions are satisfied, the relevant data for each section is recorded in main_dict for further analysis. This meticulous approach allows for a more accurate assessment of tree canopy changes, which is vital for effective urban forestry management and environmental monitoring.

In [214]:
main_id_2019=[]
main_id_2018=[]
main_id_2021=[]
main_id_2016=[]
visited=[]
main_dict={'geo_point_2018':[], 'geo_shape_2018':[], 'id_2018':[],
       'area_2018':[], 'geo_point_2019':[], 'geo_shape_2019':[], 'id_2019':[], 'area_2019':[],
       'geo_point_2021':[], 'geo_shape_2021':[], 'area_2021':[], 'geometry':[],'geo_point_2016':[],'geo_shape_2016':[],'area_2016':[]}
for i,row in overlap_2018_2019_2021_2016.iterrows():
    
    id_2018=row['objectid']
    if id_2018 not in visited:
        visited.append(id_2018)
        geo_section=overlap_2018_2019_2021_2016.loc[overlap_2018_2019_2021_2016['objectid']==id_2018]
        lst_2019=[]
        sum_2019=0
        idx=[]
        for j,k in geo_section.iterrows():
            if k['id'] not in lst_2019:
                lst_2019.append(k['id'])
                sum_2019+=k['area_2019']
                idx.append(j)
        lst_2021=[]
        sum_2021=0
        for j,k in geo_section.iterrows():
            if k['geo_point_2d'] not in lst_2021:
                lst_2021.append(k['geo_point_2d'])
                sum_2021+=k['area_2021']
                idx.append(j)
        lst_2016=[]
        sum_2016=0
        for j,k in geo_section.iterrows():
            if k['geo_point_2016'] not in lst_2016:
                lst_2016.append(k['geo_point_2016'])
                sum_2016+=k['area_2016']
                idx.append(j)
        if sum_2016>row['area_2018'] and row['area_2018']>sum_2019 and sum_2019>sum_2021:
            main_id_2018.append(id_2018)
            main_id_2019.extend(lst_2019)
            main_id_2021.extend(lst_2021)
            main_id_2016.extend(lst_2016)
            main_dict['geo_point_2018'].append(overlap_2018_2019_2021_2016.iloc[j,0])
            main_dict['geo_shape_2018'].append(overlap_2018_2019_2021_2016.iloc[j,1])
            main_dict['id_2018'].append(overlap_2018_2019_2021_2016.iloc[j,2])
            main_dict['area_2018'].append(overlap_2018_2019_2021_2016.iloc[j,5])
            main_dict['geo_point_2019'].append(overlap_2018_2019_2021_2016.iloc[j,6])
            main_dict['geo_shape_2019'].append(overlap_2018_2019_2021_2016.iloc[j,7])
            main_dict['id_2019'].append(overlap_2018_2019_2021_2016.iloc[j,8])
            main_dict['area_2019'].append(overlap_2018_2019_2021_2016.iloc[j,9])
            main_dict['geo_point_2021'].append(overlap_2018_2019_2021_2016.iloc[j,10])
            main_dict['geo_shape_2021'].append(overlap_2018_2019_2021_2016.iloc[j,11])
            main_dict['area_2021'].append(overlap_2018_2019_2021_2016.iloc[j,12])
            main_dict['geometry'].append(overlap_2018_2019_2021_2016.iloc[j,17])
            main_dict['geo_point_2016'].append(overlap_2018_2019_2021_2016.iloc[j,13])
            main_dict['geo_shape_2016'].append(overlap_2018_2019_2021_2016.iloc[j,14])
            main_dict['area_2016'].append(overlap_2018_2019_2021_2016.iloc[j,16])

Finally, the code constructs a new GeoDataFrame 'gdf' and setting its coordinate reference systems to EPSG:4326.

In [215]:
gdf=gpd.GeoDataFrame(main_dict, geometry='geometry')
gdf.crs='EPSG:4326'

The GeoDataFrames are filtered to retain only the entries that correspond to the identified overlapping sections stored in main_id_2018, main_id_2019, main_id_2021, and main_id_2016.

In [216]:
gdf_2018=gdf_2018.loc[gdf_2018['objectid'].isin(main_id_2018)]
gdf_2019=gdf_2019.loc[gdf_2019['id'].isin(main_id_2019)]
gdf_2021=gdf_2021.loc[gdf_2021['geo_point_2d'].isin(main_id_2021)]
gdf_2016=gdf_2016.loc[gdf_2016['geo_point_2016'].isin(main_id_2016)]
In [217]:
gdf_2018['geometry_2018'] = gdf_2018['geometry_2018'].to_crs('EPSG:4326')
gdf_2019['geometry_2019'] = gdf_2019['geometry_2019'].to_crs('EPSG:4326')
gdf_2021['geometry_2021'] = gdf_2021['geometry_2021'].to_crs('EPSG:4326')
gdf_2016['geometry_2016'] = gdf_2016['geometry_2016'].to_crs('EPSG:4326')
In [218]:
gdf.head(3)
Out[218]:
geo_point_2018 geo_shape_2018 id_2018 area_2018 geo_point_2019 geo_shape_2019 id_2019 area_2019 geo_point_2021 geo_shape_2021 area_2021 geometry geo_point_2016 geo_shape_2016 area_2016
0 -37.84484005800719, 144.9799763642485 {"coordinates": [[[[144.9799995022198, -37.844... 619 49.275435 -37.84483829849872, 144.9799805672743 {"coordinates": [[[144.9799850896, -37.8448682... 113125 33.812505 -37.844837503719205, 144.97997250091714 {"coordinates": [[[[144.97996946968496, -37.84... 30.413232 POLYGON ((322264.07142 5809474.75704, 322263.9... -37.84484005706343, 144.97997636406888 {"coordinates": [[[144.97999950204024, -37.844... 49.275435
1 -37.84417531045389, 144.98522392545587 {"coordinates": [[[[144.98522128543277, -37.84... 719 69.668075 -37.84417049851317, 144.9852278575925 {"coordinates": [[[144.9852472073, -37.8442240... 112524 68.687494 -37.84417271724014, 144.98521982951164 {"coordinates": [[[[144.98521945705122, -37.84... 48.211200 MULTIPOLYGON (((322729.66220 5809561.50000, 32... -37.84417530951022, 144.98522392527988 {"coordinates": [[[144.9852212852568, -37.8441... 69.668075
2 -37.843803178769505, 144.98487209410104 {"coordinates": [[[[144.98488411105262, -37.84... 758 26.447354 -37.84383309560812, 144.98486853767145 {"coordinates": [[[144.9848740841, -37.8438413... 112442 1.601554 -37.84383192443206, 144.9848627194639 {"coordinates": [[[[144.98485919857444, -37.84... 1.094400 POLYGON ((322692.69642 5809598.13204, 322692.5... -37.84380317782593, 144.9848720939247 {"coordinates": [[[144.98488411087644, -37.843... 26.447354

Visualizing decrease in tree canopy coverage from 2018 to 2021¶

Folium library is used to create an interactive map that visualizes changes in tree canopy areas over the years 2018, 2019, and 2021. Each year is represented by a different color: blue for 2018, green for 2019, and red for 2021. The polygons are styled with a fill color and transparency, allowing for a visual overlay of the canopy areas. Additionally, tooltips and popups display the unique IDs of the shapes, making it easier to identify and interact with specific areas on the map. By overlaying these layers, the map visually illustrates how tree canopy coverage has decreased over the years, highlighting areas of significant change. 2016 was not included in the map visualization as it is too far back in time for the current analysis, but it has been retained for potential future reference if needed.

In [219]:
gdf_2021
Out[219]:
geo_point_2d geo_shape geometry_2021 area_2021
110 -37.790928887533376, 144.94521647983152 {"coordinates": [[[[144.94521433161924, -37.79... MULTIPOLYGON (((144.94521 -37.79092, 144.94522... 0.864072
145 -37.77673736152948, 144.94560914165618 {"coordinates": [[[[144.94562473589923, -37.77... MULTIPOLYGON (((144.94562 -37.77670, 144.94564... 41.415048
179 -37.77703241502789, 144.9485129190978 {"coordinates": [[[[144.94850402197275, -37.77... MULTIPOLYGON (((144.94850 -37.77703, 144.94852... 2.880096
188 -37.79285981604331, 144.95222136178748 {"coordinates": [[[[144.95221987054111, -37.79... MULTIPOLYGON (((144.95222 -37.79285, 144.95222... 0.576000
250 -37.79615639997903, 144.97004049433275 {"coordinates": [[[[144.97003610480002, -37.79... MULTIPOLYGON (((144.97004 -37.79615, 144.97004... 0.748800
... ... ... ... ...
57940 -37.834153017682326, 144.9805954085748 {"coordinates": [[[[144.9805988673201, -37.834... MULTIPOLYGON (((144.98060 -37.83413, 144.98060... 11.750400
57946 -37.83297189227819, 144.9096400760473 {"coordinates": [[[[144.90963697163548, -37.83... MULTIPOLYGON (((144.90964 -37.83296, 144.90964... 1.843200
57948 -37.83409966658709, 144.97503871275174 {"coordinates": [[[[144.9750048195116, -37.834... MULTIPOLYGON (((144.97500 -37.83407, 144.97501... 42.105600
57949 -37.83298936815527, 144.9097548823628 {"coordinates": [[[[144.90975089773997, -37.83... MULTIPOLYGON (((144.90975 -37.83299, 144.90976... 0.576072
57954 -37.83300250254527, 144.90976102599538 {"coordinates": [[[[144.90975059263388, -37.83... MULTIPOLYGON (((144.90975 -37.83300, 144.90976... 1.036800

4608 rows × 4 columns

In [220]:
# p=gdf_2018.copy()
# q=gdf_2019.copy()
# r=gdf_2021.copy()
# p['geometry_2018']=gdf_2018['geometry_2018'].simplify(tolerance=2, preserve_topology=True)
# q['geometry_2019']=gdf_2019['geometry_2019'].simplify(tolerance=2, preserve_topology=True)
# r['geometry_2021']=gdf_2021['geometry_2021'].simplify(tolerance=2, preserve_topology=True)
In [221]:
m = folium.Map(location=[-37.8136, 144.9631], zoom_start=12)  

# Function to add GeoDataFrames to the map
def add_geo_to_map(gdf, color, year, id):
    geo_json = folium.GeoJson(
        gdf,
        name=year,
        style_function=lambda feature: {
            'fillColor': color,
            'color': color,
            'weight': 1,
            'fillOpacity': 0.5,
        },
        popup=folium.GeoJsonPopup(fields=[id], aliases=[f'{year} ID:']),
        
        tooltip=folium.GeoJsonTooltip(fields=[id], aliases=[f'{year} ID:'])
    ).add_to(m)

    

# Add each GeoDataFrame to the map with different colors
add_geo_to_map(p, 'blue', '2018', 'objectid')
add_geo_to_map(q, 'green', '2019', 'id')
add_geo_to_map(r, 'red', '2021', 'geo_point_2d')

# Add a layer control to toggle the layers on/off
folium.LayerControl().add_to(m)
legend_html = """
        <div style="position: fixed; 
                    bottom: 50px; left: 50px; width: 150px; height: 90px; 
                    border:2px solid grey; z-index:9999; font-size:14px;
                    background-color:white; opacity: 0.8;">
              <p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
              <p style="margin: 0;">Area 2018: <span style="color:blue">&#9679;</span></p>
              <p style="margin: 0;">Area 2019: <span style="color:green">&#9679;</span></p>
              <p style="margin: 0;">Area 2021: <span style="color:red">&#9679;</span></p>
        </div>
        """
        
m.get_root().html.add_child(folium.Element(legend_html))
        
title_html = """
    <h3 style="text-align: center; margin: 10px 0;">Decrease in tree canopy coverage from 2018 to 2021</h3>
        """
m.get_root().html.add_child(folium.Element(title_html))

m.save("Decrease_in_tree_canopy_coverage_from_2018_to_2021.html")

Link to the above map: Decrease in tree canopy coverage from 2018 to 2021

Analysis of tree canopy areas: distribution, transformation, and correlation¶

In [222]:
#gdf is copied into test to avoid data loss
test=gdf.copy()

Additional features are created in the test DataFrame to enhance the dataset for further analysis. The calculations include differences in tree canopy area between the years (e.g., diff_2016_2018, diff_2018_2019, diff_2019_2021) to capture the changes over time. Growth rates (growth_2018_2019 and growth_2019_2021) are also calculated to quantify the percentage change in area from one year to the next. Additionally, lagged values (lag_2018, lag_2019, lag_2021) are introduced to capture the previous year's areas.

In [223]:
test['diff_2016_2018']=test['area_2018']-test['area_2016']
test['diff_2018_2019']=test['area_2019']-test['area_2018']
test['diff_2019_2021']=test['area_2021']-test['area_2019']
In [224]:
test['growth_2018_2019'] = (test['area_2019'] - test['area_2018']) / test['area_2018']
test['growth_2019_2021'] = (test['area_2021'] - test['area_2019']) / test['area_2019']
In [225]:
test['lag_2018'] = test['area_2018'].shift(1)  
test['lag_2019'] = test['area_2019'].shift(1)
test['lag_2018_2019'] = test['lag_2019'] - test['lag_2018']
test['lag_2019'] = test['area_2019'].shift(1) 
test['lag_2021'] = test['area_2021'].shift(1)
test['lag_2019_2021'] = test['lag_2021'] - test['lag_2019']

Histograms display that all the features are skewed.

In [226]:
cols=['area_2016','area_2018', 'area_2019', 'area_2021','diff_2016_2018','diff_2018_2019','diff_2019_2021','growth_2018_2019','growth_2019_2021','lag_2018_2019','lag_2019_2021']
fig,axes=plt.subplots(nrows=4,ncols=3,figsize=(20, 14))
num=0
for column in cols:

    ax=axes.flatten()[num] 
    ax.hist(test[column], bins=25)
    ax.set_title(f'Histogram of {column}')
    ax.set_xlabel(column)
    ax.set_ylabel('Frequency')
    ax.grid(True)
    num+=1
fig.delaxes(axes.flatten()[-1])
plt.tight_layout()
plt.show()
No description has been provided for this image

Skewness is calculated which validates the histogram visualization.

In [227]:
skewness=test[cols].skew()
# Print the skewness of each feature
print("Skewness of Features:")
print(skewness)
Skewness of Features:
area_2016            8.623436
area_2018           12.877649
area_2019            7.699284
area_2021            7.224967
diff_2016_2018      18.161424
diff_2018_2019     -20.246171
diff_2019_2021      -5.311718
growth_2018_2019    -1.142622
growth_2019_2021    19.912258
lag_2018_2019      -20.242829
lag_2019_2021       -5.310716
dtype: float64

Yeo Johnson power transformation is applied since it works for both positive and negative data and is more robust.

In [228]:
cols1=['area_2016','area_2018', 'area_2019','diff_2016_2018','diff_2018_2019','diff_2019_2021','growth_2018_2019','growth_2019_2021','lag_2018_2019','lag_2019_2021']

pt1 = PowerTransformer(method='yeo-johnson')
yeojohnson_val = pt1.fit_transform(test[cols1])

test[cols1]=yeojohnson_val

pt2 = PowerTransformer(method='yeo-johnson')
yeojohnson_val_2021 = pt2.fit_transform(test[['area_2021']])

test[['area_2021']]=yeojohnson_val_2021
In [229]:
fig,axes=plt.subplots(nrows=4,ncols=3,figsize=(20, 14))
num=0
for column in cols:

    ax=axes.flatten()[num]
    ax.hist(test[column], bins=25)
    ax.set_title(f'Histogram of {column}')
    ax.set_xlabel(column)
    ax.set_ylabel('Frequency')
    ax.grid(True)
    num+=1
fig.delaxes(axes.flatten()[-1])
plt.tight_layout()
plt.show()
No description has been provided for this image

It can be seen that diff_2016_2018, diff_2019_2021, and lag_2019_2021 still exhibit skewness greater than 0.5, indicating potential non-normality in their distributions. However, for now, these features will be ignored in terms of further transformation, as the focus is on the primary trends and relationships within the dataset

In [230]:
skewness=test[cols].skew()
# Print the skewness of each feature
print("Skewness of Features:")
print(skewness)
Skewness of Features:
area_2016          -0.002757
area_2018          -0.000947
area_2019          -0.000527
area_2021          -0.011242
diff_2016_2018      4.202263
diff_2018_2019     -0.035863
diff_2019_2021     -1.946345
growth_2018_2019   -0.244462
growth_2019_2021   -0.098397
lag_2018_2019      -0.035935
lag_2019_2021      -1.946314
dtype: float64

The StandardScaler is first applied to standardize the features in cols, transforming them to have a mean of 0 and a standard deviation of 1. Subsequently, the MinMaxScaler is used to rescale the same features to a range between 0 and 1, ensuring the values are normalized for subsequent analysis or modeling.

In [231]:
standard_scaler = StandardScaler()
standard_scaler.fit(test[cols])
scaled=standard_scaler.transform(test[cols])

test[cols]=scaled


minmax_scaler = MinMaxScaler()
minmax_scaler.fit(test[cols])
scaled = minmax_scaler.transform(test[cols])
test[cols]=scaled

The boxplot visualizes the distribution of the transformed columns, highlighting the presence of outliers. However, these outliers are retained in the dataset, as they represent real measurements of tree canopy areas, and it's important to maintain these values for a comprehensive analysis.

In [232]:
plt.figure(figsize=(10, 6))
sns.boxplot(data=test[cols])
plt.title('Boxplots of transformed area columns')
plt.ylabel('Values')
plt.xticks(rotation=90)
plt.show()
No description has been provided for this image
In [233]:
fig,axes=plt.subplots(nrows=4,ncols=3,figsize=(20, 14))
num=0
for column in cols:

    ax=axes.flatten()[num] # Adjust figure size as needed
    ax.hist(test[column], bins=25)
    ax.set_title(f'Histogram of {column}')
    ax.set_xlabel(column)
    ax.set_ylabel('Frequency')
    ax.grid(True)
    num+=1
fig.delaxes(axes.flatten()[-1])
plt.tight_layout()
plt.show()
No description has been provided for this image
In [234]:
corr_matrix = test[cols].corr()
plt.figure(figsize=(8, 6))  # Set figure size
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', vmin=-1, vmax=1, linewidths=0.5)
plt.title('Correlation matrix heatmap')
plt.show()
No description has been provided for this image

The correlation matrix heatmap illustrates the relationships between various area measurements and derived features, revealing that the area in 2021 exhibits weak correlations with the differences and lag variables. Specifically, the low correlation values with diff_2016_2018, diff_2018_2019, diff_2019_2021, and the lag variables indicate that these features may not provide meaningful predictive information for forecasting the area in 2021. Additionally, growth_2019_2021 is excluded from consideration, as it incorporates the 2021 area values, which could bias the predictions. Consequently, 'area_2016','area_2018','area_2019','growth_2018_2019', with stronger correlations to the 2021 area will be retained for modeling purposes.

Regression model evaluation and residual analysis¶

In this analysis, various regression models were applied to predict the area in 2021 based on historical data from 2016, 2018, and 2019, as well as the growth from 2018 to 2019. The performance of each model was assessed using Mean Squared Error (MSE), Mean Absolute Error (MAE), and R-squared values.

Summary of Model Performance: | Model | Test MSE | Test MAE | Test R-squared | |-----------------------------------------|----------|----------|-----------------| | LinearRegression() | 0.0164 | 0.0909 | 0.5157 | | RandomForestRegressor(max_depth=5, n_estimators=50, random_state=42) | 0.0154 | 0.0841 | 0.5432 | | SVR() | 0.0165 | 0.0983 | 0.5118 | | GradientBoostingRegressor(max_depth=5, n_estimators=20, random_state=42) | 0.0158 | 0.0873 | 0.5320 | | RANSACRegressor(random_state=42) | 0.1048 | 0.1185 | -2.1035 | | HuberRegressor(epsilon=10) | 0.0164 | 0.0909 | 0.5157 | | PolynomialRegression() | 0.0162 | 0.0863 | 0.5210 |

The Random Forest Regressor achieved the lowest Mean Squared Error (MSE) of 0.0154 and the highest R-squared value of 0.5432, indicating that it explained more variance in the target variable (area in 2021) compared to the other models. Gradient Boosting and Polynomial follow with 0.5320 and 0.5210. The Linear Regression and Huber Regressor showed similar performance, with MSE values around 0.0164 and R-squared values around 0.5157, suggesting they are effective but not as robust as the Random Forest in capturing the relationship. The RANSAC Regressor performed poorly with a high MSE of 0.1048 and a negative R-squared value of -2.1035, indicating it failed to capture the underlying trend in the data.

In [235]:
X=test[[
 'area_2016',
 'area_2018',
 'area_2019',
 'growth_2018_2019',
 ]].values
y=test['area_2021']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Linear Regression: A simple model that provides a good baseline. However, it may not capture complex relationships in the data.

In [236]:
lin_reg = LinearRegression()
lin_reg.fit(X_train, y_train)
Out[236]:
LinearRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LinearRegression()

Random Forest Regressor: Outperformed other models with the lowest MSE and MAE. Its ability to capture nonlinearities makes it robust for this dataset.

In [237]:
rf_model = RandomForestRegressor(n_estimators=50, max_depth=5,random_state=42)
rf_model.fit(X_train, y_train)
Out[237]:
RandomForestRegressor(max_depth=5, n_estimators=50, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomForestRegressor(max_depth=5, n_estimators=50, random_state=42)

Support Vector Regressor: Similar performance to linear regression but less effective in capturing the data's underlying structure.

In [238]:
svr_model = SVR(kernel='rbf', C=1.0, epsilon=0.1)
svr_model.fit(X_train, y_train)
Out[238]:
SVR()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SVR()

Gradient Boosting Regressor: Shows competitive performance; however, it is more prone to overfitting if not tuned correctly.

In [239]:
gbr_model = GradientBoostingRegressor(n_estimators=20, learning_rate=0.1, max_depth=5, random_state=42)
gbr_model.fit(X_train, y_train)
Out[239]:
GradientBoostingRegressor(max_depth=5, n_estimators=20, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
GradientBoostingRegressor(max_depth=5, n_estimators=20, random_state=42)

RANSAC Regressor: The worst performer, likely due to its assumptions about data distribution and outliers. Generally, RANSAC is a robust regression algorithm designed to estimate parameters of a model from a dataset that contains a significant proportion of outliers, however, it is likely not appropriate for the underlying data distribution in this data.

In [240]:
ransac = RANSACRegressor(random_state=42)
ransac.fit(X_train, y_train)
Out[240]:
RANSACRegressor(random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RANSACRegressor(random_state=42)

Huber Regressor: Offers resilience against outliers and performs similarly to linear regression in this case.

In [241]:
huber = HuberRegressor(epsilon=10)  # epsilon can be adjusted based on the dataset
huber.fit(X_train, y_train)
Out[241]:
HuberRegressor(epsilon=10)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
HuberRegressor(epsilon=10)

Polynomial Regression: Slightly better performance than linear regression, indicating some non-linear relationships in the data.

In [242]:
poly = PolynomialFeatures(degree=2)
X_poly = poly.fit_transform(X)
X_train_poly, X_test_poly, y_train_poly, y_test_poly = train_test_split(X_poly, y, test_size=0.2, random_state=42)

poly_reg = LinearRegression()
poly_reg.fit(X_train_poly, y_train_poly)
Out[242]:
LinearRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LinearRegression()

The residuals were plotted against predicted values and their distribution was visualized through histograms. Analyzing residuals helps to evaluate model assumptions and performance. Understanding the distribution and patterns in residuals is vital for improving model accuracy and diagnosing potential issues. The analysis revealed Heteroscedasticity as all models exhibited a funnel shape in the residuals plot, suggesting that variance in errors increases with the predicted value. This is a critical aspect to consider, as it violates the assumption of homoscedasticity in linear regression.

In [243]:
models=[lin_reg,rf_model,svr_model,gbr_model,ransac,huber,poly_reg]
scores=[]
plt.figure(figsize=(14, len(models)*6)) 

for i, model in enumerate(models, 1):
    if model==poly_reg:
        X_test=X_test_poly
        y_test=y_test_poly
    y_pred = model.predict(X_test)
    
    mse = mean_squared_error(y_test, y_pred)
    mae = mean_absolute_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)

    scores.append([mse,mae,r2])

    test_residuals = y_test - y_pred

    # Create a subplot for each model's residual plots
    plt.subplot(len(models), 2, 2*i-1)
    plt.scatter(y_pred, test_residuals, alpha=0.5)
    plt.hlines(y=0, xmin=np.min(y_pred), xmax=np.max(y_pred), colors='r', linestyles='--')
    plt.xlabel('Predicted Values')
    plt.ylabel('Residuals')
    plt.title(f'{model.__class__.__name__} Residuals vs. Predicted Values')

    plt.subplot(len(models), 2, 2*i)
    plt.hist(test_residuals, bins=50, alpha=0.75, color='b')
    plt.xlabel('Residuals')
    plt.ylabel('Frequency')
    plt.title(f'{model.__class__.__name__} Distribution of Residuals')

plt.tight_layout()
plt.show()
No description has been provided for this image
In [244]:
for i, model_score in enumerate(scores):
    print(f"{models[i]}:")
    print(f"  Test MSE: {model_score[0]:.4f}")
    print(f"  Test MAE: {model_score[1]:.4f}")
    print(f"  Test R-squared: {model_score[2]:.4f}")
    print("-" * 30)
LinearRegression():
  Test MSE: 0.0164
  Test MAE: 0.0909
  Test R-squared: 0.5157
------------------------------
RandomForestRegressor(max_depth=5, n_estimators=50, random_state=42):
  Test MSE: 0.0154
  Test MAE: 0.0841
  Test R-squared: 0.5432
------------------------------
SVR():
  Test MSE: 0.0165
  Test MAE: 0.0983
  Test R-squared: 0.5118
------------------------------
GradientBoostingRegressor(max_depth=5, n_estimators=20, random_state=42):
  Test MSE: 0.0158
  Test MAE: 0.0873
  Test R-squared: 0.5320
------------------------------
RANSACRegressor(random_state=42):
  Test MSE: 0.1048
  Test MAE: 0.1185
  Test R-squared: -2.1035
------------------------------
HuberRegressor(epsilon=10):
  Test MSE: 0.0164
  Test MAE: 0.0909
  Test R-squared: 0.5157
------------------------------
LinearRegression():
  Test MSE: 0.0162
  Test MAE: 0.0863
  Test R-squared: 0.5210
------------------------------

Neural network regression model for area prediction¶

This code implements a neural network regression model to predict the area in 2021 based on earlier area data and growth metrics. The model is trained using the Keras library with TensorFlow as the backend.

This function defines a custom R-squared metric that calculates the goodness of fit of the model. It computes the residual sum of squares (the difference between the predicted and actual values) and the total sum of squares (the variance of the actual values), returning the R-squared score.

In [245]:
def r2_score_nn(y_true, y_pred):
    ss_res = K.sum(K.square(y_true - y_pred))  # Residual sum of squares
    ss_tot = K.sum(K.square(y_true - K.mean(y_true)))  # Total sum of squares
    return 1 - ss_res / (ss_tot + K.epsilon())

Here, the feature matrix X and target vector y are prepared using 'area_2016', 'area_2018', 'area_2019', 'growth_2018_2019'. The dataset is split into training and testing sets using an 80-20 split.

In [246]:
X=test[[
 'area_2016',
 'area_2018',
 'area_2019',
 'growth_2018_2019',
 ]].values

y=test['area_2021']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

The neural network consists of:

  • An input layer that matches the number of features.
  • Hidden layers with varying sizes and ReLU activation functions. The architecture gradually decreases the number of neurons from 300 to 15(300,150,60,30,15) to extract complex patterns in the data.
  • An output layer with a single neuron and a linear activation function to predict the continuous target variable (area in 2021).
  • The model is compiled with the Adadelta optimizer(Adadelta optimizer is used since it is has adaptive learning rate) and mean squared error (MSE) as the loss function. Additionally, it tracks the Mean Absolute Error (MAE) and the custom R-squared metric during training.
  • Early stopping is implemented to prevent overfitting by monitoring the validation MAE. If the validation loss does not improve for 10 epochs, training stops, and the best model weights are restored.
  • The model is trained for up to 550 epochs with a batch size of 20. It uses 20% of the training data for validation to monitor performance.
In [247]:
input_layer = Input(shape=(X_train.shape[1],))

x = Dense(300, activation='relu')(input_layer)  # L2 regularization
x = Dense(150, activation='relu')(x)
x = Dense(60, activation='relu')(x)
x = Dense(30, activation='relu')(x)
x = Dense(15, activation='relu')(x)

output_layer = Dense(1, activation='linear')(x)

model = Model(inputs=input_layer, outputs=output_layer)

model.compile(optimizer=Adadelta(), loss='mse',metrics=['mae',r2_score_nn])

model.summary()
WARNING:absl:At this time, the v2.11+ optimizer `tf.keras.optimizers.Adadelta` runs slowly on M1/M2 Macs, please use the legacy Keras optimizer instead, located at `tf.keras.optimizers.legacy.Adadelta`.
WARNING:absl:There is a known slowdown when using v2.11+ Keras optimizers on M1/M2 Macs. Falling back to the legacy Keras optimizer, i.e., `tf.keras.optimizers.legacy.Adadelta`.
Model: "model_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_3 (InputLayer)        [(None, 4)]               0         
                                                                 
 dense_12 (Dense)            (None, 300)               1500      
                                                                 
 dense_13 (Dense)            (None, 150)               45150     
                                                                 
 dense_14 (Dense)            (None, 60)                9060      
                                                                 
 dense_15 (Dense)            (None, 30)                1830      
                                                                 
 dense_16 (Dense)            (None, 15)                465       
                                                                 
 dense_17 (Dense)            (None, 1)                 16        
                                                                 
=================================================================
Total params: 58,021
Trainable params: 58,021
Non-trainable params: 0
_________________________________________________________________
In [248]:
early_stopping = EarlyStopping(monitor='val_mae', patience=10, restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=550, batch_size=20, validation_split=0.2,callbacks=[early_stopping])
Epoch 1/550
94/94 [==============================] - 0s 2ms/step - loss: 0.1053 - mae: 0.2815 - r2_score_nn: -2.1450 - val_loss: 0.1065 - val_mae: 0.2849 - val_r2_score_nn: -2.1065
Epoch 2/550
94/94 [==============================] - 0s 839us/step - loss: 0.0980 - mae: 0.2711 - r2_score_nn: -1.9044 - val_loss: 0.0988 - val_mae: 0.2740 - val_r2_score_nn: -1.8784
Epoch 3/550
94/94 [==============================] - 0s 915us/step - loss: 0.0902 - mae: 0.2599 - r2_score_nn: -1.6268 - val_loss: 0.0908 - val_mae: 0.2623 - val_r2_score_nn: -1.6422
Epoch 4/550
94/94 [==============================] - 0s 919us/step - loss: 0.0826 - mae: 0.2485 - r2_score_nn: -1.4265 - val_loss: 0.0832 - val_mae: 0.2508 - val_r2_score_nn: -1.4165
Epoch 5/550
94/94 [==============================] - 0s 895us/step - loss: 0.0752 - mae: 0.2371 - r2_score_nn: -1.1933 - val_loss: 0.0756 - val_mae: 0.2389 - val_r2_score_nn: -1.1931
Epoch 6/550
94/94 [==============================] - 0s 921us/step - loss: 0.0680 - mae: 0.2254 - r2_score_nn: -1.0103 - val_loss: 0.0685 - val_mae: 0.2270 - val_r2_score_nn: -0.9806
Epoch 7/550
94/94 [==============================] - 0s 914us/step - loss: 0.0613 - mae: 0.2141 - r2_score_nn: -0.7894 - val_loss: 0.0619 - val_mae: 0.2155 - val_r2_score_nn: -0.7857
Epoch 8/550
94/94 [==============================] - 0s 912us/step - loss: 0.0552 - mae: 0.2031 - r2_score_nn: -0.6371 - val_loss: 0.0560 - val_mae: 0.2045 - val_r2_score_nn: -0.6085
Epoch 9/550
94/94 [==============================] - 0s 917us/step - loss: 0.0498 - mae: 0.1926 - r2_score_nn: -0.4720 - val_loss: 0.0506 - val_mae: 0.1940 - val_r2_score_nn: -0.4497
Epoch 10/550
94/94 [==============================] - 0s 919us/step - loss: 0.0449 - mae: 0.1827 - r2_score_nn: -0.2945 - val_loss: 0.0458 - val_mae: 0.1839 - val_r2_score_nn: -0.3067
Epoch 11/550
94/94 [==============================] - 0s 911us/step - loss: 0.0406 - mae: 0.1733 - r2_score_nn: -0.1751 - val_loss: 0.0417 - val_mae: 0.1745 - val_r2_score_nn: -0.1823
Epoch 12/550
94/94 [==============================] - 0s 912us/step - loss: 0.0369 - mae: 0.1645 - r2_score_nn: -0.0892 - val_loss: 0.0381 - val_mae: 0.1659 - val_r2_score_nn: -0.0757
Epoch 13/550
94/94 [==============================] - 0s 910us/step - loss: 0.0339 - mae: 0.1568 - r2_score_nn: -3.5137e-04 - val_loss: 0.0351 - val_mae: 0.1580 - val_r2_score_nn: 0.0138
Epoch 14/550
94/94 [==============================] - 0s 903us/step - loss: 0.0313 - mae: 0.1498 - r2_score_nn: 0.0868 - val_loss: 0.0327 - val_mae: 0.1510 - val_r2_score_nn: 0.0881
Epoch 15/550
94/94 [==============================] - 0s 902us/step - loss: 0.0292 - mae: 0.1435 - r2_score_nn: 0.1671 - val_loss: 0.0307 - val_mae: 0.1447 - val_r2_score_nn: 0.1497
Epoch 16/550
94/94 [==============================] - 0s 882us/step - loss: 0.0275 - mae: 0.1380 - r2_score_nn: 0.2109 - val_loss: 0.0290 - val_mae: 0.1393 - val_r2_score_nn: 0.1987
Epoch 17/550
94/94 [==============================] - 0s 898us/step - loss: 0.0262 - mae: 0.1334 - r2_score_nn: 0.2669 - val_loss: 0.0277 - val_mae: 0.1345 - val_r2_score_nn: 0.2390
Epoch 18/550
94/94 [==============================] - 0s 919us/step - loss: 0.0252 - mae: 0.1291 - r2_score_nn: 0.2858 - val_loss: 0.0267 - val_mae: 0.1306 - val_r2_score_nn: 0.2707
Epoch 19/550
94/94 [==============================] - 0s 913us/step - loss: 0.0244 - mae: 0.1255 - r2_score_nn: 0.3124 - val_loss: 0.0259 - val_mae: 0.1273 - val_r2_score_nn: 0.2957
Epoch 20/550
94/94 [==============================] - 0s 895us/step - loss: 0.0237 - mae: 0.1225 - r2_score_nn: 0.3332 - val_loss: 0.0253 - val_mae: 0.1245 - val_r2_score_nn: 0.3152
Epoch 21/550
94/94 [==============================] - 0s 913us/step - loss: 0.0233 - mae: 0.1199 - r2_score_nn: 0.3350 - val_loss: 0.0248 - val_mae: 0.1222 - val_r2_score_nn: 0.3295
Epoch 22/550
94/94 [==============================] - 0s 898us/step - loss: 0.0230 - mae: 0.1179 - r2_score_nn: 0.3514 - val_loss: 0.0245 - val_mae: 0.1203 - val_r2_score_nn: 0.3405
Epoch 23/550
94/94 [==============================] - 0s 906us/step - loss: 0.0227 - mae: 0.1163 - r2_score_nn: 0.3747 - val_loss: 0.0243 - val_mae: 0.1187 - val_r2_score_nn: 0.3490
Epoch 24/550
94/94 [==============================] - 0s 910us/step - loss: 0.0225 - mae: 0.1149 - r2_score_nn: 0.3726 - val_loss: 0.0241 - val_mae: 0.1173 - val_r2_score_nn: 0.3552
Epoch 25/550
94/94 [==============================] - 0s 906us/step - loss: 0.0224 - mae: 0.1137 - r2_score_nn: 0.3763 - val_loss: 0.0239 - val_mae: 0.1162 - val_r2_score_nn: 0.3600
Epoch 26/550
94/94 [==============================] - 0s 919us/step - loss: 0.0223 - mae: 0.1130 - r2_score_nn: 0.3722 - val_loss: 0.0238 - val_mae: 0.1153 - val_r2_score_nn: 0.3637
Epoch 27/550
94/94 [==============================] - 0s 913us/step - loss: 0.0222 - mae: 0.1122 - r2_score_nn: 0.3796 - val_loss: 0.0237 - val_mae: 0.1146 - val_r2_score_nn: 0.3665
Epoch 28/550
94/94 [==============================] - 0s 912us/step - loss: 0.0222 - mae: 0.1117 - r2_score_nn: 0.3717 - val_loss: 0.0237 - val_mae: 0.1140 - val_r2_score_nn: 0.3688
Epoch 29/550
94/94 [==============================] - 0s 917us/step - loss: 0.0222 - mae: 0.1112 - r2_score_nn: 0.3667 - val_loss: 0.0236 - val_mae: 0.1135 - val_r2_score_nn: 0.3705
Epoch 30/550
94/94 [==============================] - 0s 915us/step - loss: 0.0221 - mae: 0.1108 - r2_score_nn: 0.3805 - val_loss: 0.0236 - val_mae: 0.1131 - val_r2_score_nn: 0.3719
Epoch 31/550
94/94 [==============================] - 0s 910us/step - loss: 0.0221 - mae: 0.1105 - r2_score_nn: 0.3835 - val_loss: 0.0235 - val_mae: 0.1127 - val_r2_score_nn: 0.3732
Epoch 32/550
94/94 [==============================] - 0s 900us/step - loss: 0.0221 - mae: 0.1102 - r2_score_nn: 0.3834 - val_loss: 0.0235 - val_mae: 0.1125 - val_r2_score_nn: 0.3741
Epoch 33/550
94/94 [==============================] - 0s 916us/step - loss: 0.0220 - mae: 0.1099 - r2_score_nn: 0.3945 - val_loss: 0.0235 - val_mae: 0.1122 - val_r2_score_nn: 0.3750
Epoch 34/550
94/94 [==============================] - 0s 918us/step - loss: 0.0220 - mae: 0.1097 - r2_score_nn: 0.3710 - val_loss: 0.0235 - val_mae: 0.1121 - val_r2_score_nn: 0.3757
Epoch 35/550
94/94 [==============================] - 0s 897us/step - loss: 0.0220 - mae: 0.1096 - r2_score_nn: 0.3690 - val_loss: 0.0234 - val_mae: 0.1119 - val_r2_score_nn: 0.3764
Epoch 36/550
94/94 [==============================] - 0s 912us/step - loss: 0.0220 - mae: 0.1093 - r2_score_nn: 0.3682 - val_loss: 0.0234 - val_mae: 0.1118 - val_r2_score_nn: 0.3770
Epoch 37/550
94/94 [==============================] - 0s 917us/step - loss: 0.0220 - mae: 0.1092 - r2_score_nn: 0.3800 - val_loss: 0.0234 - val_mae: 0.1116 - val_r2_score_nn: 0.3776
Epoch 38/550
94/94 [==============================] - 0s 914us/step - loss: 0.0219 - mae: 0.1091 - r2_score_nn: 0.3745 - val_loss: 0.0234 - val_mae: 0.1115 - val_r2_score_nn: 0.3781
Epoch 39/550
94/94 [==============================] - 0s 904us/step - loss: 0.0219 - mae: 0.1091 - r2_score_nn: 0.3934 - val_loss: 0.0234 - val_mae: 0.1113 - val_r2_score_nn: 0.3787
Epoch 40/550
94/94 [==============================] - 0s 902us/step - loss: 0.0219 - mae: 0.1089 - r2_score_nn: 0.3798 - val_loss: 0.0234 - val_mae: 0.1112 - val_r2_score_nn: 0.3792
Epoch 41/550
94/94 [==============================] - 0s 912us/step - loss: 0.0219 - mae: 0.1088 - r2_score_nn: 0.3924 - val_loss: 0.0233 - val_mae: 0.1111 - val_r2_score_nn: 0.3798
Epoch 42/550
94/94 [==============================] - 0s 906us/step - loss: 0.0219 - mae: 0.1086 - r2_score_nn: 0.4022 - val_loss: 0.0233 - val_mae: 0.1110 - val_r2_score_nn: 0.3802
Epoch 43/550
94/94 [==============================] - 0s 913us/step - loss: 0.0218 - mae: 0.1086 - r2_score_nn: 0.3981 - val_loss: 0.0233 - val_mae: 0.1108 - val_r2_score_nn: 0.3808
Epoch 44/550
94/94 [==============================] - 0s 903us/step - loss: 0.0218 - mae: 0.1085 - r2_score_nn: 0.3817 - val_loss: 0.0233 - val_mae: 0.1107 - val_r2_score_nn: 0.3813
Epoch 45/550
94/94 [==============================] - 0s 909us/step - loss: 0.0218 - mae: 0.1083 - r2_score_nn: 0.3958 - val_loss: 0.0233 - val_mae: 0.1106 - val_r2_score_nn: 0.3819
Epoch 46/550
94/94 [==============================] - 0s 909us/step - loss: 0.0218 - mae: 0.1083 - r2_score_nn: 0.4000 - val_loss: 0.0233 - val_mae: 0.1105 - val_r2_score_nn: 0.3824
Epoch 47/550
94/94 [==============================] - 0s 906us/step - loss: 0.0218 - mae: 0.1081 - r2_score_nn: 0.4029 - val_loss: 0.0232 - val_mae: 0.1104 - val_r2_score_nn: 0.3828
Epoch 48/550
94/94 [==============================] - 0s 905us/step - loss: 0.0218 - mae: 0.1080 - r2_score_nn: 0.3929 - val_loss: 0.0232 - val_mae: 0.1103 - val_r2_score_nn: 0.3833
Epoch 49/550
94/94 [==============================] - 0s 901us/step - loss: 0.0217 - mae: 0.1080 - r2_score_nn: 0.3768 - val_loss: 0.0232 - val_mae: 0.1102 - val_r2_score_nn: 0.3837
Epoch 50/550
94/94 [==============================] - 0s 909us/step - loss: 0.0217 - mae: 0.1080 - r2_score_nn: 0.3848 - val_loss: 0.0232 - val_mae: 0.1101 - val_r2_score_nn: 0.3842
Epoch 51/550
94/94 [==============================] - 0s 874us/step - loss: 0.0217 - mae: 0.1078 - r2_score_nn: 0.4004 - val_loss: 0.0232 - val_mae: 0.1101 - val_r2_score_nn: 0.3846
Epoch 52/550
94/94 [==============================] - 0s 913us/step - loss: 0.0217 - mae: 0.1077 - r2_score_nn: 0.3875 - val_loss: 0.0232 - val_mae: 0.1100 - val_r2_score_nn: 0.3850
Epoch 53/550
94/94 [==============================] - 0s 916us/step - loss: 0.0217 - mae: 0.1077 - r2_score_nn: 0.4021 - val_loss: 0.0232 - val_mae: 0.1098 - val_r2_score_nn: 0.3856
Epoch 54/550
94/94 [==============================] - 0s 904us/step - loss: 0.0217 - mae: 0.1076 - r2_score_nn: 0.3916 - val_loss: 0.0231 - val_mae: 0.1097 - val_r2_score_nn: 0.3861
Epoch 55/550
94/94 [==============================] - 0s 903us/step - loss: 0.0217 - mae: 0.1075 - r2_score_nn: 0.3808 - val_loss: 0.0231 - val_mae: 0.1097 - val_r2_score_nn: 0.3865
Epoch 56/550
94/94 [==============================] - 0s 912us/step - loss: 0.0216 - mae: 0.1074 - r2_score_nn: 0.4067 - val_loss: 0.0231 - val_mae: 0.1096 - val_r2_score_nn: 0.3870
Epoch 57/550
94/94 [==============================] - 0s 916us/step - loss: 0.0216 - mae: 0.1073 - r2_score_nn: 0.3858 - val_loss: 0.0231 - val_mae: 0.1095 - val_r2_score_nn: 0.3874
Epoch 58/550
94/94 [==============================] - 0s 905us/step - loss: 0.0216 - mae: 0.1072 - r2_score_nn: 0.3983 - val_loss: 0.0231 - val_mae: 0.1094 - val_r2_score_nn: 0.3879
Epoch 59/550
94/94 [==============================] - 0s 913us/step - loss: 0.0216 - mae: 0.1073 - r2_score_nn: 0.3971 - val_loss: 0.0231 - val_mae: 0.1094 - val_r2_score_nn: 0.3882
Epoch 60/550
94/94 [==============================] - 0s 906us/step - loss: 0.0216 - mae: 0.1072 - r2_score_nn: 0.3955 - val_loss: 0.0230 - val_mae: 0.1093 - val_r2_score_nn: 0.3887
Epoch 61/550
94/94 [==============================] - 0s 906us/step - loss: 0.0216 - mae: 0.1070 - r2_score_nn: 0.3973 - val_loss: 0.0230 - val_mae: 0.1092 - val_r2_score_nn: 0.3892
Epoch 62/550
94/94 [==============================] - 0s 907us/step - loss: 0.0215 - mae: 0.1071 - r2_score_nn: 0.3837 - val_loss: 0.0230 - val_mae: 0.1091 - val_r2_score_nn: 0.3897
Epoch 63/550
94/94 [==============================] - 0s 914us/step - loss: 0.0215 - mae: 0.1070 - r2_score_nn: 0.3973 - val_loss: 0.0230 - val_mae: 0.1090 - val_r2_score_nn: 0.3902
Epoch 64/550
94/94 [==============================] - 0s 913us/step - loss: 0.0215 - mae: 0.1068 - r2_score_nn: 0.3775 - val_loss: 0.0230 - val_mae: 0.1090 - val_r2_score_nn: 0.3905
Epoch 65/550
94/94 [==============================] - 0s 907us/step - loss: 0.0215 - mae: 0.1068 - r2_score_nn: 0.3931 - val_loss: 0.0230 - val_mae: 0.1089 - val_r2_score_nn: 0.3908
Epoch 66/550
94/94 [==============================] - 0s 916us/step - loss: 0.0215 - mae: 0.1068 - r2_score_nn: 0.3924 - val_loss: 0.0230 - val_mae: 0.1088 - val_r2_score_nn: 0.3912
Epoch 67/550
94/94 [==============================] - 0s 905us/step - loss: 0.0215 - mae: 0.1067 - r2_score_nn: 0.4027 - val_loss: 0.0230 - val_mae: 0.1088 - val_r2_score_nn: 0.3916
Epoch 68/550
94/94 [==============================] - 0s 908us/step - loss: 0.0215 - mae: 0.1065 - r2_score_nn: 0.3899 - val_loss: 0.0229 - val_mae: 0.1087 - val_r2_score_nn: 0.3920
Epoch 69/550
94/94 [==============================] - 0s 912us/step - loss: 0.0215 - mae: 0.1067 - r2_score_nn: 0.3979 - val_loss: 0.0229 - val_mae: 0.1086 - val_r2_score_nn: 0.3924
Epoch 70/550
94/94 [==============================] - 0s 889us/step - loss: 0.0214 - mae: 0.1066 - r2_score_nn: 0.3975 - val_loss: 0.0229 - val_mae: 0.1085 - val_r2_score_nn: 0.3928
Epoch 71/550
94/94 [==============================] - 0s 907us/step - loss: 0.0214 - mae: 0.1065 - r2_score_nn: 0.4050 - val_loss: 0.0229 - val_mae: 0.1085 - val_r2_score_nn: 0.3932
Epoch 72/550
94/94 [==============================] - 0s 905us/step - loss: 0.0214 - mae: 0.1064 - r2_score_nn: 0.4033 - val_loss: 0.0229 - val_mae: 0.1084 - val_r2_score_nn: 0.3935
Epoch 73/550
94/94 [==============================] - 0s 887us/step - loss: 0.0214 - mae: 0.1063 - r2_score_nn: 0.4033 - val_loss: 0.0229 - val_mae: 0.1084 - val_r2_score_nn: 0.3937
Epoch 74/550
94/94 [==============================] - 0s 904us/step - loss: 0.0214 - mae: 0.1064 - r2_score_nn: 0.4021 - val_loss: 0.0229 - val_mae: 0.1083 - val_r2_score_nn: 0.3941
Epoch 75/550
94/94 [==============================] - 0s 896us/step - loss: 0.0214 - mae: 0.1064 - r2_score_nn: 0.3935 - val_loss: 0.0229 - val_mae: 0.1082 - val_r2_score_nn: 0.3946
Epoch 76/550
94/94 [==============================] - 0s 893us/step - loss: 0.0214 - mae: 0.1062 - r2_score_nn: 0.4090 - val_loss: 0.0228 - val_mae: 0.1081 - val_r2_score_nn: 0.3950
Epoch 77/550
94/94 [==============================] - 0s 891us/step - loss: 0.0214 - mae: 0.1061 - r2_score_nn: 0.4004 - val_loss: 0.0228 - val_mae: 0.1081 - val_r2_score_nn: 0.3954
Epoch 78/550
94/94 [==============================] - 0s 895us/step - loss: 0.0213 - mae: 0.1060 - r2_score_nn: 0.4082 - val_loss: 0.0228 - val_mae: 0.1080 - val_r2_score_nn: 0.3957
Epoch 79/550
94/94 [==============================] - 0s 894us/step - loss: 0.0213 - mae: 0.1058 - r2_score_nn: 0.3965 - val_loss: 0.0228 - val_mae: 0.1080 - val_r2_score_nn: 0.3960
Epoch 80/550
94/94 [==============================] - 0s 914us/step - loss: 0.0213 - mae: 0.1060 - r2_score_nn: 0.4000 - val_loss: 0.0228 - val_mae: 0.1079 - val_r2_score_nn: 0.3963
Epoch 81/550
94/94 [==============================] - 0s 884us/step - loss: 0.0213 - mae: 0.1058 - r2_score_nn: 0.4068 - val_loss: 0.0228 - val_mae: 0.1079 - val_r2_score_nn: 0.3966
Epoch 82/550
94/94 [==============================] - 0s 890us/step - loss: 0.0213 - mae: 0.1059 - r2_score_nn: 0.3924 - val_loss: 0.0228 - val_mae: 0.1077 - val_r2_score_nn: 0.3970
Epoch 83/550
94/94 [==============================] - 0s 909us/step - loss: 0.0213 - mae: 0.1056 - r2_score_nn: 0.3877 - val_loss: 0.0228 - val_mae: 0.1078 - val_r2_score_nn: 0.3972
Epoch 84/550
94/94 [==============================] - 0s 890us/step - loss: 0.0213 - mae: 0.1057 - r2_score_nn: 0.4006 - val_loss: 0.0227 - val_mae: 0.1077 - val_r2_score_nn: 0.3976
Epoch 85/550
94/94 [==============================] - 0s 907us/step - loss: 0.0213 - mae: 0.1058 - r2_score_nn: 0.3970 - val_loss: 0.0227 - val_mae: 0.1076 - val_r2_score_nn: 0.3980
Epoch 86/550
94/94 [==============================] - 0s 911us/step - loss: 0.0213 - mae: 0.1057 - r2_score_nn: 0.4229 - val_loss: 0.0227 - val_mae: 0.1075 - val_r2_score_nn: 0.3985
Epoch 87/550
94/94 [==============================] - 0s 903us/step - loss: 0.0213 - mae: 0.1055 - r2_score_nn: 0.4085 - val_loss: 0.0227 - val_mae: 0.1074 - val_r2_score_nn: 0.3987
Epoch 88/550
94/94 [==============================] - 0s 904us/step - loss: 0.0212 - mae: 0.1055 - r2_score_nn: 0.4081 - val_loss: 0.0227 - val_mae: 0.1073 - val_r2_score_nn: 0.3991
Epoch 89/550
94/94 [==============================] - 0s 898us/step - loss: 0.0212 - mae: 0.1055 - r2_score_nn: 0.3925 - val_loss: 0.0227 - val_mae: 0.1073 - val_r2_score_nn: 0.3995
Epoch 90/550
94/94 [==============================] - 0s 899us/step - loss: 0.0212 - mae: 0.1054 - r2_score_nn: 0.3999 - val_loss: 0.0227 - val_mae: 0.1072 - val_r2_score_nn: 0.3998
Epoch 91/550
94/94 [==============================] - 0s 906us/step - loss: 0.0212 - mae: 0.1053 - r2_score_nn: 0.3984 - val_loss: 0.0227 - val_mae: 0.1072 - val_r2_score_nn: 0.4000
Epoch 92/550
94/94 [==============================] - 0s 899us/step - loss: 0.0212 - mae: 0.1052 - r2_score_nn: 0.4014 - val_loss: 0.0227 - val_mae: 0.1071 - val_r2_score_nn: 0.4003
Epoch 93/550
94/94 [==============================] - 0s 880us/step - loss: 0.0212 - mae: 0.1051 - r2_score_nn: 0.4125 - val_loss: 0.0226 - val_mae: 0.1071 - val_r2_score_nn: 0.4006
Epoch 94/550
94/94 [==============================] - 0s 910us/step - loss: 0.0212 - mae: 0.1053 - r2_score_nn: 0.4060 - val_loss: 0.0226 - val_mae: 0.1070 - val_r2_score_nn: 0.4009
Epoch 95/550
94/94 [==============================] - 0s 910us/step - loss: 0.0212 - mae: 0.1051 - r2_score_nn: 0.4002 - val_loss: 0.0226 - val_mae: 0.1070 - val_r2_score_nn: 0.4012
Epoch 96/550
94/94 [==============================] - 0s 908us/step - loss: 0.0212 - mae: 0.1051 - r2_score_nn: 0.4150 - val_loss: 0.0226 - val_mae: 0.1069 - val_r2_score_nn: 0.4015
Epoch 97/550
94/94 [==============================] - 0s 899us/step - loss: 0.0212 - mae: 0.1051 - r2_score_nn: 0.3964 - val_loss: 0.0226 - val_mae: 0.1068 - val_r2_score_nn: 0.4018
Epoch 98/550
94/94 [==============================] - 0s 885us/step - loss: 0.0211 - mae: 0.1050 - r2_score_nn: 0.4101 - val_loss: 0.0226 - val_mae: 0.1068 - val_r2_score_nn: 0.4021
Epoch 99/550
94/94 [==============================] - 0s 896us/step - loss: 0.0211 - mae: 0.1050 - r2_score_nn: 0.4251 - val_loss: 0.0226 - val_mae: 0.1067 - val_r2_score_nn: 0.4024
Epoch 100/550
94/94 [==============================] - 0s 920us/step - loss: 0.0211 - mae: 0.1049 - r2_score_nn: 0.4154 - val_loss: 0.0226 - val_mae: 0.1066 - val_r2_score_nn: 0.4027
Epoch 101/550
94/94 [==============================] - 0s 900us/step - loss: 0.0211 - mae: 0.1048 - r2_score_nn: 0.3999 - val_loss: 0.0226 - val_mae: 0.1066 - val_r2_score_nn: 0.4029
Epoch 102/550
94/94 [==============================] - 0s 911us/step - loss: 0.0211 - mae: 0.1049 - r2_score_nn: 0.4077 - val_loss: 0.0226 - val_mae: 0.1065 - val_r2_score_nn: 0.4032
Epoch 103/550
94/94 [==============================] - 0s 901us/step - loss: 0.0211 - mae: 0.1048 - r2_score_nn: 0.4104 - val_loss: 0.0225 - val_mae: 0.1065 - val_r2_score_nn: 0.4035
Epoch 104/550
94/94 [==============================] - 0s 902us/step - loss: 0.0211 - mae: 0.1049 - r2_score_nn: 0.3933 - val_loss: 0.0225 - val_mae: 0.1064 - val_r2_score_nn: 0.4039
Epoch 105/550
94/94 [==============================] - 0s 899us/step - loss: 0.0211 - mae: 0.1046 - r2_score_nn: 0.4140 - val_loss: 0.0225 - val_mae: 0.1063 - val_r2_score_nn: 0.4041
Epoch 106/550
94/94 [==============================] - 0s 912us/step - loss: 0.0211 - mae: 0.1046 - r2_score_nn: 0.4124 - val_loss: 0.0225 - val_mae: 0.1063 - val_r2_score_nn: 0.4044
Epoch 107/550
94/94 [==============================] - 0s 909us/step - loss: 0.0211 - mae: 0.1045 - r2_score_nn: 0.4159 - val_loss: 0.0225 - val_mae: 0.1062 - val_r2_score_nn: 0.4047
Epoch 108/550
94/94 [==============================] - 0s 906us/step - loss: 0.0211 - mae: 0.1046 - r2_score_nn: 0.4164 - val_loss: 0.0225 - val_mae: 0.1062 - val_r2_score_nn: 0.4050
Epoch 109/550
94/94 [==============================] - 0s 887us/step - loss: 0.0210 - mae: 0.1044 - r2_score_nn: 0.4129 - val_loss: 0.0225 - val_mae: 0.1061 - val_r2_score_nn: 0.4052
Epoch 110/550
94/94 [==============================] - 0s 914us/step - loss: 0.0210 - mae: 0.1045 - r2_score_nn: 0.4124 - val_loss: 0.0225 - val_mae: 0.1061 - val_r2_score_nn: 0.4055
Epoch 111/550
94/94 [==============================] - 0s 883us/step - loss: 0.0210 - mae: 0.1044 - r2_score_nn: 0.4093 - val_loss: 0.0225 - val_mae: 0.1059 - val_r2_score_nn: 0.4059
Epoch 112/550
94/94 [==============================] - 0s 899us/step - loss: 0.0210 - mae: 0.1042 - r2_score_nn: 0.4161 - val_loss: 0.0225 - val_mae: 0.1059 - val_r2_score_nn: 0.4061
Epoch 113/550
94/94 [==============================] - 0s 898us/step - loss: 0.0210 - mae: 0.1042 - r2_score_nn: 0.4021 - val_loss: 0.0225 - val_mae: 0.1059 - val_r2_score_nn: 0.4063
Epoch 114/550
94/94 [==============================] - 0s 906us/step - loss: 0.0210 - mae: 0.1042 - r2_score_nn: 0.3954 - val_loss: 0.0224 - val_mae: 0.1059 - val_r2_score_nn: 0.4065
Epoch 115/550
94/94 [==============================] - 0s 899us/step - loss: 0.0210 - mae: 0.1042 - r2_score_nn: 0.4050 - val_loss: 0.0224 - val_mae: 0.1058 - val_r2_score_nn: 0.4068
Epoch 116/550
94/94 [==============================] - 0s 914us/step - loss: 0.0210 - mae: 0.1042 - r2_score_nn: 0.4034 - val_loss: 0.0224 - val_mae: 0.1058 - val_r2_score_nn: 0.4070
Epoch 117/550
94/94 [==============================] - 0s 900us/step - loss: 0.0210 - mae: 0.1042 - r2_score_nn: 0.4095 - val_loss: 0.0224 - val_mae: 0.1057 - val_r2_score_nn: 0.4073
Epoch 118/550
94/94 [==============================] - 0s 908us/step - loss: 0.0210 - mae: 0.1040 - r2_score_nn: 0.4200 - val_loss: 0.0224 - val_mae: 0.1057 - val_r2_score_nn: 0.4075
Epoch 119/550
94/94 [==============================] - 0s 879us/step - loss: 0.0210 - mae: 0.1041 - r2_score_nn: 0.4186 - val_loss: 0.0224 - val_mae: 0.1055 - val_r2_score_nn: 0.4079
Epoch 120/550
94/94 [==============================] - 0s 888us/step - loss: 0.0210 - mae: 0.1039 - r2_score_nn: 0.4163 - val_loss: 0.0224 - val_mae: 0.1056 - val_r2_score_nn: 0.4080
Epoch 121/550
94/94 [==============================] - 0s 888us/step - loss: 0.0209 - mae: 0.1040 - r2_score_nn: 0.4047 - val_loss: 0.0224 - val_mae: 0.1055 - val_r2_score_nn: 0.4083
Epoch 122/550
94/94 [==============================] - 0s 904us/step - loss: 0.0209 - mae: 0.1040 - r2_score_nn: 0.4199 - val_loss: 0.0224 - val_mae: 0.1054 - val_r2_score_nn: 0.4085
Epoch 123/550
94/94 [==============================] - 0s 903us/step - loss: 0.0209 - mae: 0.1040 - r2_score_nn: 0.4123 - val_loss: 0.0224 - val_mae: 0.1054 - val_r2_score_nn: 0.4088
Epoch 124/550
94/94 [==============================] - 0s 888us/step - loss: 0.0209 - mae: 0.1038 - r2_score_nn: 0.4182 - val_loss: 0.0224 - val_mae: 0.1053 - val_r2_score_nn: 0.4090
Epoch 125/550
94/94 [==============================] - 0s 893us/step - loss: 0.0209 - mae: 0.1037 - r2_score_nn: 0.4236 - val_loss: 0.0224 - val_mae: 0.1053 - val_r2_score_nn: 0.4091
Epoch 126/550
94/94 [==============================] - 0s 862us/step - loss: 0.0209 - mae: 0.1036 - r2_score_nn: 0.4166 - val_loss: 0.0223 - val_mae: 0.1053 - val_r2_score_nn: 0.4094
Epoch 127/550
94/94 [==============================] - 0s 900us/step - loss: 0.0209 - mae: 0.1038 - r2_score_nn: 0.4121 - val_loss: 0.0223 - val_mae: 0.1052 - val_r2_score_nn: 0.4096
Epoch 128/550
94/94 [==============================] - 0s 886us/step - loss: 0.0209 - mae: 0.1037 - r2_score_nn: 0.4207 - val_loss: 0.0223 - val_mae: 0.1052 - val_r2_score_nn: 0.4099
Epoch 129/550
94/94 [==============================] - 0s 885us/step - loss: 0.0209 - mae: 0.1036 - r2_score_nn: 0.4069 - val_loss: 0.0223 - val_mae: 0.1051 - val_r2_score_nn: 0.4101
Epoch 130/550
94/94 [==============================] - 0s 893us/step - loss: 0.0209 - mae: 0.1034 - r2_score_nn: 0.4104 - val_loss: 0.0223 - val_mae: 0.1051 - val_r2_score_nn: 0.4102
Epoch 131/550
94/94 [==============================] - 0s 903us/step - loss: 0.0209 - mae: 0.1037 - r2_score_nn: 0.4067 - val_loss: 0.0223 - val_mae: 0.1051 - val_r2_score_nn: 0.4104
Epoch 132/550
94/94 [==============================] - 0s 908us/step - loss: 0.0209 - mae: 0.1036 - r2_score_nn: 0.4281 - val_loss: 0.0223 - val_mae: 0.1050 - val_r2_score_nn: 0.4107
Epoch 133/550
94/94 [==============================] - 0s 882us/step - loss: 0.0209 - mae: 0.1034 - r2_score_nn: 0.4198 - val_loss: 0.0223 - val_mae: 0.1049 - val_r2_score_nn: 0.4110
Epoch 134/550
94/94 [==============================] - 0s 885us/step - loss: 0.0208 - mae: 0.1035 - r2_score_nn: 0.4321 - val_loss: 0.0223 - val_mae: 0.1049 - val_r2_score_nn: 0.4113
Epoch 135/550
94/94 [==============================] - 0s 915us/step - loss: 0.0208 - mae: 0.1033 - r2_score_nn: 0.4216 - val_loss: 0.0223 - val_mae: 0.1048 - val_r2_score_nn: 0.4115
Epoch 136/550
94/94 [==============================] - 0s 882us/step - loss: 0.0208 - mae: 0.1034 - r2_score_nn: 0.4176 - val_loss: 0.0223 - val_mae: 0.1048 - val_r2_score_nn: 0.4117
Epoch 137/550
94/94 [==============================] - 0s 894us/step - loss: 0.0208 - mae: 0.1034 - r2_score_nn: 0.4124 - val_loss: 0.0223 - val_mae: 0.1047 - val_r2_score_nn: 0.4119
Epoch 138/550
94/94 [==============================] - 0s 897us/step - loss: 0.0208 - mae: 0.1032 - r2_score_nn: 0.4058 - val_loss: 0.0223 - val_mae: 0.1047 - val_r2_score_nn: 0.4120
Epoch 139/550
94/94 [==============================] - 0s 908us/step - loss: 0.0208 - mae: 0.1033 - r2_score_nn: 0.4046 - val_loss: 0.0222 - val_mae: 0.1047 - val_r2_score_nn: 0.4123
Epoch 140/550
94/94 [==============================] - 0s 901us/step - loss: 0.0208 - mae: 0.1033 - r2_score_nn: 0.4056 - val_loss: 0.0222 - val_mae: 0.1046 - val_r2_score_nn: 0.4126
Epoch 141/550
94/94 [==============================] - 0s 891us/step - loss: 0.0208 - mae: 0.1032 - r2_score_nn: 0.4229 - val_loss: 0.0222 - val_mae: 0.1045 - val_r2_score_nn: 0.4128
Epoch 142/550
94/94 [==============================] - 0s 883us/step - loss: 0.0208 - mae: 0.1031 - r2_score_nn: 0.3961 - val_loss: 0.0222 - val_mae: 0.1045 - val_r2_score_nn: 0.4130
Epoch 143/550
94/94 [==============================] - 0s 897us/step - loss: 0.0208 - mae: 0.1033 - r2_score_nn: 0.4120 - val_loss: 0.0222 - val_mae: 0.1044 - val_r2_score_nn: 0.4133
Epoch 144/550
94/94 [==============================] - 0s 903us/step - loss: 0.0208 - mae: 0.1032 - r2_score_nn: 0.4064 - val_loss: 0.0222 - val_mae: 0.1043 - val_r2_score_nn: 0.4136
Epoch 145/550
94/94 [==============================] - 0s 909us/step - loss: 0.0208 - mae: 0.1030 - r2_score_nn: 0.4124 - val_loss: 0.0222 - val_mae: 0.1043 - val_r2_score_nn: 0.4138
Epoch 146/550
94/94 [==============================] - 0s 914us/step - loss: 0.0208 - mae: 0.1029 - r2_score_nn: 0.4274 - val_loss: 0.0222 - val_mae: 0.1042 - val_r2_score_nn: 0.4140
Epoch 147/550
94/94 [==============================] - 0s 891us/step - loss: 0.0208 - mae: 0.1029 - r2_score_nn: 0.4123 - val_loss: 0.0222 - val_mae: 0.1042 - val_r2_score_nn: 0.4142
Epoch 148/550
94/94 [==============================] - 0s 900us/step - loss: 0.0207 - mae: 0.1028 - r2_score_nn: 0.4136 - val_loss: 0.0222 - val_mae: 0.1041 - val_r2_score_nn: 0.4145
Epoch 149/550
94/94 [==============================] - 0s 910us/step - loss: 0.0207 - mae: 0.1029 - r2_score_nn: 0.4079 - val_loss: 0.0222 - val_mae: 0.1041 - val_r2_score_nn: 0.4146
Epoch 150/550
94/94 [==============================] - 0s 911us/step - loss: 0.0207 - mae: 0.1029 - r2_score_nn: 0.4274 - val_loss: 0.0222 - val_mae: 0.1041 - val_r2_score_nn: 0.4148
Epoch 151/550
94/94 [==============================] - 0s 911us/step - loss: 0.0207 - mae: 0.1028 - r2_score_nn: 0.4121 - val_loss: 0.0222 - val_mae: 0.1040 - val_r2_score_nn: 0.4151
Epoch 152/550
94/94 [==============================] - 0s 912us/step - loss: 0.0207 - mae: 0.1027 - r2_score_nn: 0.4168 - val_loss: 0.0221 - val_mae: 0.1040 - val_r2_score_nn: 0.4153
Epoch 153/550
94/94 [==============================] - 0s 814us/step - loss: 0.0207 - mae: 0.1027 - r2_score_nn: 0.4237 - val_loss: 0.0221 - val_mae: 0.1039 - val_r2_score_nn: 0.4155
Epoch 154/550
94/94 [==============================] - 0s 905us/step - loss: 0.0207 - mae: 0.1025 - r2_score_nn: 0.4220 - val_loss: 0.0221 - val_mae: 0.1039 - val_r2_score_nn: 0.4157
Epoch 155/550
94/94 [==============================] - 0s 899us/step - loss: 0.0207 - mae: 0.1025 - r2_score_nn: 0.4155 - val_loss: 0.0221 - val_mae: 0.1039 - val_r2_score_nn: 0.4159
Epoch 156/550
94/94 [==============================] - 0s 902us/step - loss: 0.0207 - mae: 0.1026 - r2_score_nn: 0.4299 - val_loss: 0.0221 - val_mae: 0.1039 - val_r2_score_nn: 0.4160
Epoch 157/550
94/94 [==============================] - 0s 910us/step - loss: 0.0207 - mae: 0.1026 - r2_score_nn: 0.4396 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4164
Epoch 158/550
94/94 [==============================] - 0s 893us/step - loss: 0.0207 - mae: 0.1025 - r2_score_nn: 0.4173 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4165
Epoch 159/550
94/94 [==============================] - 0s 905us/step - loss: 0.0207 - mae: 0.1025 - r2_score_nn: 0.4104 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4166
Epoch 160/550
94/94 [==============================] - 0s 903us/step - loss: 0.0207 - mae: 0.1023 - r2_score_nn: 0.3990 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4168
Epoch 161/550
94/94 [==============================] - 0s 979us/step - loss: 0.0207 - mae: 0.1023 - r2_score_nn: 0.4369 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4168
Epoch 162/550
94/94 [==============================] - 0s 924us/step - loss: 0.0207 - mae: 0.1024 - r2_score_nn: 0.4205 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4170
Epoch 163/550
94/94 [==============================] - 0s 977us/step - loss: 0.0207 - mae: 0.1024 - r2_score_nn: 0.4163 - val_loss: 0.0221 - val_mae: 0.1037 - val_r2_score_nn: 0.4172
Epoch 164/550
94/94 [==============================] - 0s 914us/step - loss: 0.0206 - mae: 0.1024 - r2_score_nn: 0.4265 - val_loss: 0.0221 - val_mae: 0.1036 - val_r2_score_nn: 0.4175
Epoch 165/550
94/94 [==============================] - 0s 932us/step - loss: 0.0206 - mae: 0.1024 - r2_score_nn: 0.4159 - val_loss: 0.0221 - val_mae: 0.1036 - val_r2_score_nn: 0.4177
Epoch 166/550
94/94 [==============================] - 0s 924us/step - loss: 0.0206 - mae: 0.1023 - r2_score_nn: 0.4008 - val_loss: 0.0221 - val_mae: 0.1035 - val_r2_score_nn: 0.4178
Epoch 167/550
94/94 [==============================] - 0s 915us/step - loss: 0.0206 - mae: 0.1024 - r2_score_nn: 0.4259 - val_loss: 0.0220 - val_mae: 0.1035 - val_r2_score_nn: 0.4180
Epoch 168/550
94/94 [==============================] - 0s 913us/step - loss: 0.0206 - mae: 0.1023 - r2_score_nn: 0.4217 - val_loss: 0.0220 - val_mae: 0.1034 - val_r2_score_nn: 0.4182
Epoch 169/550
94/94 [==============================] - 0s 916us/step - loss: 0.0206 - mae: 0.1023 - r2_score_nn: 0.4175 - val_loss: 0.0220 - val_mae: 0.1033 - val_r2_score_nn: 0.4185
Epoch 170/550
94/94 [==============================] - 0s 918us/step - loss: 0.0206 - mae: 0.1022 - r2_score_nn: 0.4126 - val_loss: 0.0220 - val_mae: 0.1033 - val_r2_score_nn: 0.4188
Epoch 171/550
94/94 [==============================] - 0s 910us/step - loss: 0.0206 - mae: 0.1021 - r2_score_nn: 0.4318 - val_loss: 0.0220 - val_mae: 0.1032 - val_r2_score_nn: 0.4189
Epoch 172/550
94/94 [==============================] - 0s 913us/step - loss: 0.0206 - mae: 0.1021 - r2_score_nn: 0.4228 - val_loss: 0.0220 - val_mae: 0.1032 - val_r2_score_nn: 0.4191
Epoch 173/550
94/94 [==============================] - 0s 920us/step - loss: 0.0206 - mae: 0.1021 - r2_score_nn: 0.4258 - val_loss: 0.0220 - val_mae: 0.1031 - val_r2_score_nn: 0.4194
Epoch 174/550
94/94 [==============================] - 0s 922us/step - loss: 0.0206 - mae: 0.1018 - r2_score_nn: 0.4155 - val_loss: 0.0220 - val_mae: 0.1031 - val_r2_score_nn: 0.4195
Epoch 175/550
94/94 [==============================] - 0s 897us/step - loss: 0.0206 - mae: 0.1019 - r2_score_nn: 0.4187 - val_loss: 0.0220 - val_mae: 0.1031 - val_r2_score_nn: 0.4197
Epoch 176/550
94/94 [==============================] - 0s 882us/step - loss: 0.0206 - mae: 0.1021 - r2_score_nn: 0.4330 - val_loss: 0.0220 - val_mae: 0.1030 - val_r2_score_nn: 0.4200
Epoch 177/550
94/94 [==============================] - 0s 872us/step - loss: 0.0206 - mae: 0.1018 - r2_score_nn: 0.4310 - val_loss: 0.0220 - val_mae: 0.1030 - val_r2_score_nn: 0.4201
Epoch 178/550
94/94 [==============================] - 0s 857us/step - loss: 0.0206 - mae: 0.1020 - r2_score_nn: 0.4272 - val_loss: 0.0220 - val_mae: 0.1029 - val_r2_score_nn: 0.4203
Epoch 179/550
94/94 [==============================] - 0s 853us/step - loss: 0.0206 - mae: 0.1018 - r2_score_nn: 0.4201 - val_loss: 0.0220 - val_mae: 0.1029 - val_r2_score_nn: 0.4204
Epoch 180/550
94/94 [==============================] - 0s 823us/step - loss: 0.0206 - mae: 0.1021 - r2_score_nn: 0.4319 - val_loss: 0.0220 - val_mae: 0.1028 - val_r2_score_nn: 0.4207
Epoch 181/550
94/94 [==============================] - 0s 867us/step - loss: 0.0205 - mae: 0.1018 - r2_score_nn: 0.4271 - val_loss: 0.0220 - val_mae: 0.1028 - val_r2_score_nn: 0.4209
Epoch 182/550
94/94 [==============================] - 0s 913us/step - loss: 0.0205 - mae: 0.1017 - r2_score_nn: 0.4387 - val_loss: 0.0219 - val_mae: 0.1028 - val_r2_score_nn: 0.4210
Epoch 183/550
94/94 [==============================] - 0s 918us/step - loss: 0.0205 - mae: 0.1018 - r2_score_nn: 0.4190 - val_loss: 0.0219 - val_mae: 0.1028 - val_r2_score_nn: 0.4211
Epoch 184/550
94/94 [==============================] - 0s 888us/step - loss: 0.0205 - mae: 0.1018 - r2_score_nn: 0.4169 - val_loss: 0.0219 - val_mae: 0.1027 - val_r2_score_nn: 0.4213
Epoch 185/550
94/94 [==============================] - 0s 903us/step - loss: 0.0205 - mae: 0.1018 - r2_score_nn: 0.4288 - val_loss: 0.0219 - val_mae: 0.1027 - val_r2_score_nn: 0.4214
Epoch 186/550
94/94 [==============================] - 0s 881us/step - loss: 0.0205 - mae: 0.1017 - r2_score_nn: 0.4127 - val_loss: 0.0219 - val_mae: 0.1027 - val_r2_score_nn: 0.4216
Epoch 187/550
94/94 [==============================] - 0s 822us/step - loss: 0.0205 - mae: 0.1017 - r2_score_nn: 0.4246 - val_loss: 0.0219 - val_mae: 0.1027 - val_r2_score_nn: 0.4217
Epoch 188/550
94/94 [==============================] - 0s 909us/step - loss: 0.0205 - mae: 0.1018 - r2_score_nn: 0.4273 - val_loss: 0.0219 - val_mae: 0.1026 - val_r2_score_nn: 0.4220
Epoch 189/550
94/94 [==============================] - 0s 909us/step - loss: 0.0205 - mae: 0.1016 - r2_score_nn: 0.4264 - val_loss: 0.0219 - val_mae: 0.1025 - val_r2_score_nn: 0.4221
Epoch 190/550
94/94 [==============================] - 0s 910us/step - loss: 0.0205 - mae: 0.1016 - r2_score_nn: 0.4159 - val_loss: 0.0219 - val_mae: 0.1025 - val_r2_score_nn: 0.4223
Epoch 191/550
94/94 [==============================] - 0s 917us/step - loss: 0.0205 - mae: 0.1015 - r2_score_nn: 0.4201 - val_loss: 0.0219 - val_mae: 0.1024 - val_r2_score_nn: 0.4225
Epoch 192/550
94/94 [==============================] - 0s 908us/step - loss: 0.0205 - mae: 0.1014 - r2_score_nn: 0.4350 - val_loss: 0.0219 - val_mae: 0.1024 - val_r2_score_nn: 0.4227
Epoch 193/550
94/94 [==============================] - 0s 880us/step - loss: 0.0205 - mae: 0.1015 - r2_score_nn: 0.4305 - val_loss: 0.0219 - val_mae: 0.1024 - val_r2_score_nn: 0.4228
Epoch 194/550
94/94 [==============================] - 0s 909us/step - loss: 0.0205 - mae: 0.1014 - r2_score_nn: 0.4078 - val_loss: 0.0219 - val_mae: 0.1024 - val_r2_score_nn: 0.4229
Epoch 195/550
94/94 [==============================] - 0s 899us/step - loss: 0.0205 - mae: 0.1014 - r2_score_nn: 0.4132 - val_loss: 0.0219 - val_mae: 0.1024 - val_r2_score_nn: 0.4230
Epoch 196/550
94/94 [==============================] - 0s 905us/step - loss: 0.0205 - mae: 0.1015 - r2_score_nn: 0.4169 - val_loss: 0.0219 - val_mae: 0.1024 - val_r2_score_nn: 0.4231
Epoch 197/550
94/94 [==============================] - 0s 882us/step - loss: 0.0205 - mae: 0.1014 - r2_score_nn: 0.4371 - val_loss: 0.0219 - val_mae: 0.1023 - val_r2_score_nn: 0.4233
Epoch 198/550
94/94 [==============================] - 0s 900us/step - loss: 0.0205 - mae: 0.1011 - r2_score_nn: 0.4162 - val_loss: 0.0219 - val_mae: 0.1023 - val_r2_score_nn: 0.4233
Epoch 199/550
94/94 [==============================] - 0s 912us/step - loss: 0.0205 - mae: 0.1014 - r2_score_nn: 0.4376 - val_loss: 0.0219 - val_mae: 0.1023 - val_r2_score_nn: 0.4235
Epoch 200/550
94/94 [==============================] - 0s 912us/step - loss: 0.0205 - mae: 0.1013 - r2_score_nn: 0.4244 - val_loss: 0.0219 - val_mae: 0.1023 - val_r2_score_nn: 0.4236
Epoch 201/550
94/94 [==============================] - 0s 900us/step - loss: 0.0204 - mae: 0.1013 - r2_score_nn: 0.4159 - val_loss: 0.0218 - val_mae: 0.1022 - val_r2_score_nn: 0.4237
Epoch 202/550
94/94 [==============================] - 0s 922us/step - loss: 0.0204 - mae: 0.1013 - r2_score_nn: 0.4301 - val_loss: 0.0218 - val_mae: 0.1022 - val_r2_score_nn: 0.4239
Epoch 203/550
94/94 [==============================] - 0s 897us/step - loss: 0.0204 - mae: 0.1012 - r2_score_nn: 0.4351 - val_loss: 0.0218 - val_mae: 0.1022 - val_r2_score_nn: 0.4241
Epoch 204/550
94/94 [==============================] - 0s 909us/step - loss: 0.0204 - mae: 0.1012 - r2_score_nn: 0.4330 - val_loss: 0.0218 - val_mae: 0.1022 - val_r2_score_nn: 0.4242
Epoch 205/550
94/94 [==============================] - 0s 920us/step - loss: 0.0204 - mae: 0.1013 - r2_score_nn: 0.4183 - val_loss: 0.0218 - val_mae: 0.1021 - val_r2_score_nn: 0.4244
Epoch 206/550
94/94 [==============================] - 0s 911us/step - loss: 0.0204 - mae: 0.1012 - r2_score_nn: 0.4359 - val_loss: 0.0218 - val_mae: 0.1020 - val_r2_score_nn: 0.4246
Epoch 207/550
94/94 [==============================] - 0s 911us/step - loss: 0.0204 - mae: 0.1012 - r2_score_nn: 0.4160 - val_loss: 0.0218 - val_mae: 0.1020 - val_r2_score_nn: 0.4246
Epoch 208/550
94/94 [==============================] - 0s 915us/step - loss: 0.0204 - mae: 0.1012 - r2_score_nn: 0.4260 - val_loss: 0.0218 - val_mae: 0.1020 - val_r2_score_nn: 0.4248
Epoch 209/550
94/94 [==============================] - 0s 908us/step - loss: 0.0204 - mae: 0.1011 - r2_score_nn: 0.4386 - val_loss: 0.0218 - val_mae: 0.1019 - val_r2_score_nn: 0.4250
Epoch 210/550
94/94 [==============================] - 0s 884us/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4351 - val_loss: 0.0218 - val_mae: 0.1019 - val_r2_score_nn: 0.4251
Epoch 211/550
94/94 [==============================] - 0s 1ms/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4407 - val_loss: 0.0218 - val_mae: 0.1019 - val_r2_score_nn: 0.4252
Epoch 212/550
94/94 [==============================] - 0s 975us/step - loss: 0.0204 - mae: 0.1009 - r2_score_nn: 0.4276 - val_loss: 0.0218 - val_mae: 0.1019 - val_r2_score_nn: 0.4253
Epoch 213/550
94/94 [==============================] - 0s 939us/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4299 - val_loss: 0.0218 - val_mae: 0.1019 - val_r2_score_nn: 0.4255
Epoch 214/550
94/94 [==============================] - 0s 924us/step - loss: 0.0204 - mae: 0.1011 - r2_score_nn: 0.4307 - val_loss: 0.0218 - val_mae: 0.1018 - val_r2_score_nn: 0.4257
Epoch 215/550
94/94 [==============================] - 0s 924us/step - loss: 0.0204 - mae: 0.1011 - r2_score_nn: 0.4276 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4259
Epoch 216/550
94/94 [==============================] - 0s 923us/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4276 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4261
Epoch 217/550
94/94 [==============================] - 0s 913us/step - loss: 0.0204 - mae: 0.1009 - r2_score_nn: 0.4221 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4261
Epoch 218/550
94/94 [==============================] - 0s 909us/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4288 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4262
Epoch 219/550
94/94 [==============================] - 0s 901us/step - loss: 0.0204 - mae: 0.1007 - r2_score_nn: 0.4322 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4263
Epoch 220/550
94/94 [==============================] - 0s 908us/step - loss: 0.0204 - mae: 0.1007 - r2_score_nn: 0.4245 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4263
Epoch 221/550
94/94 [==============================] - 0s 910us/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4252 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4264
Epoch 222/550
94/94 [==============================] - 0s 916us/step - loss: 0.0204 - mae: 0.1008 - r2_score_nn: 0.4389 - val_loss: 0.0218 - val_mae: 0.1017 - val_r2_score_nn: 0.4265
Epoch 223/550
94/94 [==============================] - 0s 893us/step - loss: 0.0204 - mae: 0.1010 - r2_score_nn: 0.4483 - val_loss: 0.0217 - val_mae: 0.1016 - val_r2_score_nn: 0.4267
Epoch 224/550
94/94 [==============================] - 0s 918us/step - loss: 0.0203 - mae: 0.1008 - r2_score_nn: 0.4214 - val_loss: 0.0217 - val_mae: 0.1016 - val_r2_score_nn: 0.4269
Epoch 225/550
94/94 [==============================] - 0s 916us/step - loss: 0.0203 - mae: 0.1007 - r2_score_nn: 0.4435 - val_loss: 0.0217 - val_mae: 0.1015 - val_r2_score_nn: 0.4270
Epoch 226/550
94/94 [==============================] - 0s 907us/step - loss: 0.0203 - mae: 0.1008 - r2_score_nn: 0.4207 - val_loss: 0.0217 - val_mae: 0.1015 - val_r2_score_nn: 0.4273
Epoch 227/550
94/94 [==============================] - 0s 915us/step - loss: 0.0203 - mae: 0.1008 - r2_score_nn: 0.4240 - val_loss: 0.0217 - val_mae: 0.1014 - val_r2_score_nn: 0.4274
Epoch 228/550
94/94 [==============================] - 0s 902us/step - loss: 0.0203 - mae: 0.1008 - r2_score_nn: 0.4501 - val_loss: 0.0217 - val_mae: 0.1013 - val_r2_score_nn: 0.4277
Epoch 229/550
94/94 [==============================] - 0s 916us/step - loss: 0.0203 - mae: 0.1007 - r2_score_nn: 0.4247 - val_loss: 0.0217 - val_mae: 0.1012 - val_r2_score_nn: 0.4279
Epoch 230/550
94/94 [==============================] - 0s 900us/step - loss: 0.0203 - mae: 0.1004 - r2_score_nn: 0.4126 - val_loss: 0.0217 - val_mae: 0.1013 - val_r2_score_nn: 0.4278
Epoch 231/550
94/94 [==============================] - 0s 887us/step - loss: 0.0203 - mae: 0.1007 - r2_score_nn: 0.4270 - val_loss: 0.0217 - val_mae: 0.1013 - val_r2_score_nn: 0.4280
Epoch 232/550
94/94 [==============================] - 0s 911us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4332 - val_loss: 0.0217 - val_mae: 0.1012 - val_r2_score_nn: 0.4281
Epoch 233/550
94/94 [==============================] - 0s 915us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4348 - val_loss: 0.0217 - val_mae: 0.1013 - val_r2_score_nn: 0.4282
Epoch 234/550
94/94 [==============================] - 0s 909us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4439 - val_loss: 0.0217 - val_mae: 0.1012 - val_r2_score_nn: 0.4283
Epoch 235/550
94/94 [==============================] - 0s 909us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4298 - val_loss: 0.0217 - val_mae: 0.1012 - val_r2_score_nn: 0.4284
Epoch 236/550
94/94 [==============================] - 0s 918us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4351 - val_loss: 0.0217 - val_mae: 0.1012 - val_r2_score_nn: 0.4286
Epoch 237/550
94/94 [==============================] - 0s 888us/step - loss: 0.0203 - mae: 0.1006 - r2_score_nn: 0.4295 - val_loss: 0.0217 - val_mae: 0.1011 - val_r2_score_nn: 0.4288
Epoch 238/550
94/94 [==============================] - 0s 908us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4258 - val_loss: 0.0217 - val_mae: 0.1010 - val_r2_score_nn: 0.4290
Epoch 239/550
94/94 [==============================] - 0s 906us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4244 - val_loss: 0.0217 - val_mae: 0.1010 - val_r2_score_nn: 0.4291
Epoch 240/550
94/94 [==============================] - 0s 920us/step - loss: 0.0203 - mae: 0.1005 - r2_score_nn: 0.4376 - val_loss: 0.0217 - val_mae: 0.1009 - val_r2_score_nn: 0.4292
Epoch 241/550
94/94 [==============================] - 0s 898us/step - loss: 0.0203 - mae: 0.1002 - r2_score_nn: 0.4282 - val_loss: 0.0217 - val_mae: 0.1009 - val_r2_score_nn: 0.4293
Epoch 242/550
94/94 [==============================] - 0s 914us/step - loss: 0.0203 - mae: 0.1002 - r2_score_nn: 0.4278 - val_loss: 0.0217 - val_mae: 0.1009 - val_r2_score_nn: 0.4294
Epoch 243/550
94/94 [==============================] - 0s 913us/step - loss: 0.0203 - mae: 0.1004 - r2_score_nn: 0.4336 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4296
Epoch 244/550
94/94 [==============================] - 0s 905us/step - loss: 0.0203 - mae: 0.1003 - r2_score_nn: 0.4335 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4297
Epoch 245/550
94/94 [==============================] - 0s 864us/step - loss: 0.0203 - mae: 0.1004 - r2_score_nn: 0.4301 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4298
Epoch 246/550
94/94 [==============================] - 0s 858us/step - loss: 0.0203 - mae: 0.1002 - r2_score_nn: 0.4384 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4299
Epoch 247/550
94/94 [==============================] - 0s 905us/step - loss: 0.0203 - mae: 0.1002 - r2_score_nn: 0.4212 - val_loss: 0.0216 - val_mae: 0.1009 - val_r2_score_nn: 0.4298
Epoch 248/550
94/94 [==============================] - 0s 891us/step - loss: 0.0203 - mae: 0.1003 - r2_score_nn: 0.4351 - val_loss: 0.0216 - val_mae: 0.1009 - val_r2_score_nn: 0.4299
Epoch 249/550
94/94 [==============================] - 0s 871us/step - loss: 0.0203 - mae: 0.1003 - r2_score_nn: 0.4299 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4301
Epoch 250/550
94/94 [==============================] - 0s 909us/step - loss: 0.0202 - mae: 0.1000 - r2_score_nn: 0.4249 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4301
Epoch 251/550
94/94 [==============================] - 0s 879us/step - loss: 0.0202 - mae: 0.1004 - r2_score_nn: 0.4249 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4304
Epoch 252/550
94/94 [==============================] - 0s 872us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4153 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4304
Epoch 253/550
94/94 [==============================] - 0s 873us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4306 - val_loss: 0.0216 - val_mae: 0.1008 - val_r2_score_nn: 0.4305
Epoch 254/550
94/94 [==============================] - 0s 884us/step - loss: 0.0202 - mae: 0.1003 - r2_score_nn: 0.4266 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4306
Epoch 255/550
94/94 [==============================] - 0s 910us/step - loss: 0.0202 - mae: 0.1002 - r2_score_nn: 0.4387 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4307
Epoch 256/550
94/94 [==============================] - 0s 884us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4394 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4308
Epoch 257/550
94/94 [==============================] - 0s 889us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4328 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4308
Epoch 258/550
94/94 [==============================] - 0s 882us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4421 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4309
Epoch 259/550
94/94 [==============================] - 0s 915us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4413 - val_loss: 0.0216 - val_mae: 0.1007 - val_r2_score_nn: 0.4310
Epoch 260/550
94/94 [==============================] - 0s 951us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4230 - val_loss: 0.0216 - val_mae: 0.1006 - val_r2_score_nn: 0.4311
Epoch 261/550
94/94 [==============================] - 0s 1ms/step - loss: 0.0202 - mae: 0.1002 - r2_score_nn: 0.4286 - val_loss: 0.0216 - val_mae: 0.1006 - val_r2_score_nn: 0.4312
Epoch 262/550
94/94 [==============================] - 0s 923us/step - loss: 0.0202 - mae: 0.1000 - r2_score_nn: 0.4411 - val_loss: 0.0216 - val_mae: 0.1005 - val_r2_score_nn: 0.4313
Epoch 263/550
94/94 [==============================] - 0s 943us/step - loss: 0.0202 - mae: 0.1000 - r2_score_nn: 0.4207 - val_loss: 0.0216 - val_mae: 0.1005 - val_r2_score_nn: 0.4315
Epoch 264/550
94/94 [==============================] - 0s 924us/step - loss: 0.0202 - mae: 0.1000 - r2_score_nn: 0.4277 - val_loss: 0.0216 - val_mae: 0.1005 - val_r2_score_nn: 0.4316
Epoch 265/550
94/94 [==============================] - 0s 909us/step - loss: 0.0202 - mae: 0.1000 - r2_score_nn: 0.4340 - val_loss: 0.0216 - val_mae: 0.1005 - val_r2_score_nn: 0.4317
Epoch 266/550
94/94 [==============================] - 0s 911us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4317 - val_loss: 0.0216 - val_mae: 0.1005 - val_r2_score_nn: 0.4317
Epoch 267/550
94/94 [==============================] - 0s 913us/step - loss: 0.0202 - mae: 0.1001 - r2_score_nn: 0.4368 - val_loss: 0.0216 - val_mae: 0.1004 - val_r2_score_nn: 0.4318
Epoch 268/550
94/94 [==============================] - 0s 916us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4268 - val_loss: 0.0216 - val_mae: 0.1004 - val_r2_score_nn: 0.4319
Epoch 269/550
94/94 [==============================] - 0s 918us/step - loss: 0.0202 - mae: 0.1000 - r2_score_nn: 0.4531 - val_loss: 0.0216 - val_mae: 0.1003 - val_r2_score_nn: 0.4321
Epoch 270/550
94/94 [==============================] - 0s 882us/step - loss: 0.0202 - mae: 0.0999 - r2_score_nn: 0.4385 - val_loss: 0.0216 - val_mae: 0.1003 - val_r2_score_nn: 0.4323
Epoch 271/550
94/94 [==============================] - 0s 914us/step - loss: 0.0202 - mae: 0.0999 - r2_score_nn: 0.4343 - val_loss: 0.0216 - val_mae: 0.1003 - val_r2_score_nn: 0.4324
Epoch 272/550
94/94 [==============================] - 0s 912us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4456 - val_loss: 0.0215 - val_mae: 0.1003 - val_r2_score_nn: 0.4325
Epoch 273/550
94/94 [==============================] - 0s 922us/step - loss: 0.0202 - mae: 0.0999 - r2_score_nn: 0.4404 - val_loss: 0.0215 - val_mae: 0.1002 - val_r2_score_nn: 0.4326
Epoch 274/550
94/94 [==============================] - 0s 903us/step - loss: 0.0202 - mae: 0.0996 - r2_score_nn: 0.4178 - val_loss: 0.0215 - val_mae: 0.1002 - val_r2_score_nn: 0.4326
Epoch 275/550
94/94 [==============================] - 0s 910us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4273 - val_loss: 0.0215 - val_mae: 0.1002 - val_r2_score_nn: 0.4327
Epoch 276/550
94/94 [==============================] - 0s 909us/step - loss: 0.0202 - mae: 0.0996 - r2_score_nn: 0.4357 - val_loss: 0.0215 - val_mae: 0.1003 - val_r2_score_nn: 0.4326
Epoch 277/550
94/94 [==============================] - 0s 897us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4291 - val_loss: 0.0215 - val_mae: 0.1003 - val_r2_score_nn: 0.4327
Epoch 278/550
94/94 [==============================] - 0s 911us/step - loss: 0.0202 - mae: 0.0997 - r2_score_nn: 0.4341 - val_loss: 0.0215 - val_mae: 0.1003 - val_r2_score_nn: 0.4327
Epoch 279/550
94/94 [==============================] - 0s 909us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4338 - val_loss: 0.0215 - val_mae: 0.1002 - val_r2_score_nn: 0.4329
Epoch 280/550
94/94 [==============================] - 0s 893us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4379 - val_loss: 0.0215 - val_mae: 0.1002 - val_r2_score_nn: 0.4329
Epoch 281/550
94/94 [==============================] - 0s 906us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4278 - val_loss: 0.0215 - val_mae: 0.1002 - val_r2_score_nn: 0.4330
Epoch 282/550
94/94 [==============================] - 0s 918us/step - loss: 0.0202 - mae: 0.0998 - r2_score_nn: 0.4212 - val_loss: 0.0215 - val_mae: 0.1001 - val_r2_score_nn: 0.4333
Epoch 283/550
94/94 [==============================] - 0s 884us/step - loss: 0.0202 - mae: 0.0997 - r2_score_nn: 0.4199 - val_loss: 0.0215 - val_mae: 0.1001 - val_r2_score_nn: 0.4332
Epoch 284/550
94/94 [==============================] - 0s 905us/step - loss: 0.0201 - mae: 0.0997 - r2_score_nn: 0.4352 - val_loss: 0.0215 - val_mae: 0.1001 - val_r2_score_nn: 0.4333
Epoch 285/550
94/94 [==============================] - 0s 889us/step - loss: 0.0201 - mae: 0.0997 - r2_score_nn: 0.4360 - val_loss: 0.0215 - val_mae: 0.1001 - val_r2_score_nn: 0.4334
Epoch 286/550
94/94 [==============================] - 0s 916us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4412 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4335
Epoch 287/550
94/94 [==============================] - 0s 913us/step - loss: 0.0201 - mae: 0.0996 - r2_score_nn: 0.4277 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4337
Epoch 288/550
94/94 [==============================] - 0s 910us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4423 - val_loss: 0.0215 - val_mae: 0.1001 - val_r2_score_nn: 0.4336
Epoch 289/550
94/94 [==============================] - 0s 887us/step - loss: 0.0201 - mae: 0.0997 - r2_score_nn: 0.4356 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4338
Epoch 290/550
94/94 [==============================] - 0s 909us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4331 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4339
Epoch 291/550
94/94 [==============================] - 0s 907us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4296 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4339
Epoch 292/550
94/94 [==============================] - 0s 905us/step - loss: 0.0201 - mae: 0.0996 - r2_score_nn: 0.4412 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4340
Epoch 293/550
94/94 [==============================] - 0s 908us/step - loss: 0.0201 - mae: 0.0997 - r2_score_nn: 0.4161 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4341
Epoch 294/550
94/94 [==============================] - 0s 905us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4232 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4341
Epoch 295/550
94/94 [==============================] - 0s 877us/step - loss: 0.0201 - mae: 0.0996 - r2_score_nn: 0.4456 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4341
Epoch 296/550
94/94 [==============================] - 0s 867us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4421 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4341
Epoch 297/550
94/94 [==============================] - 0s 923us/step - loss: 0.0201 - mae: 0.0996 - r2_score_nn: 0.4366 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4341
Epoch 298/550
94/94 [==============================] - 0s 881us/step - loss: 0.0201 - mae: 0.0997 - r2_score_nn: 0.4394 - val_loss: 0.0215 - val_mae: 0.1000 - val_r2_score_nn: 0.4343
Epoch 299/550
94/94 [==============================] - 0s 904us/step - loss: 0.0201 - mae: 0.0996 - r2_score_nn: 0.4444 - val_loss: 0.0215 - val_mae: 0.0999 - val_r2_score_nn: 0.4346
Epoch 300/550
94/94 [==============================] - 0s 904us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4317 - val_loss: 0.0215 - val_mae: 0.0999 - val_r2_score_nn: 0.4345
Epoch 301/550
94/94 [==============================] - 0s 897us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4276 - val_loss: 0.0215 - val_mae: 0.0999 - val_r2_score_nn: 0.4347
Epoch 302/550
94/94 [==============================] - 0s 910us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4436 - val_loss: 0.0215 - val_mae: 0.0998 - val_r2_score_nn: 0.4348
Epoch 303/550
94/94 [==============================] - 0s 865us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4293 - val_loss: 0.0215 - val_mae: 0.0998 - val_r2_score_nn: 0.4349
Epoch 304/550
94/94 [==============================] - 0s 858us/step - loss: 0.0201 - mae: 0.0993 - r2_score_nn: 0.4213 - val_loss: 0.0215 - val_mae: 0.0998 - val_r2_score_nn: 0.4350
Epoch 305/550
94/94 [==============================] - 0s 880us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4314 - val_loss: 0.0215 - val_mae: 0.0997 - val_r2_score_nn: 0.4351
Epoch 306/550
94/94 [==============================] - 0s 891us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4394 - val_loss: 0.0214 - val_mae: 0.0997 - val_r2_score_nn: 0.4352
Epoch 307/550
94/94 [==============================] - 0s 910us/step - loss: 0.0201 - mae: 0.0993 - r2_score_nn: 0.4456 - val_loss: 0.0214 - val_mae: 0.0998 - val_r2_score_nn: 0.4352
Epoch 308/550
94/94 [==============================] - 0s 871us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4473 - val_loss: 0.0214 - val_mae: 0.0997 - val_r2_score_nn: 0.4354
Epoch 309/550
94/94 [==============================] - 0s 892us/step - loss: 0.0201 - mae: 0.0992 - r2_score_nn: 0.4423 - val_loss: 0.0214 - val_mae: 0.0997 - val_r2_score_nn: 0.4354
Epoch 310/550
94/94 [==============================] - 0s 931us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4221 - val_loss: 0.0214 - val_mae: 0.0997 - val_r2_score_nn: 0.4354
Epoch 311/550
94/94 [==============================] - 0s 1ms/step - loss: 0.0201 - mae: 0.0993 - r2_score_nn: 0.4355 - val_loss: 0.0214 - val_mae: 0.0998 - val_r2_score_nn: 0.4354
Epoch 312/550
94/94 [==============================] - 0s 905us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4301 - val_loss: 0.0214 - val_mae: 0.0998 - val_r2_score_nn: 0.4354
Epoch 313/550
94/94 [==============================] - 0s 926us/step - loss: 0.0201 - mae: 0.0993 - r2_score_nn: 0.4425 - val_loss: 0.0214 - val_mae: 0.0997 - val_r2_score_nn: 0.4356
Epoch 314/550
94/94 [==============================] - 0s 917us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4428 - val_loss: 0.0214 - val_mae: 0.0997 - val_r2_score_nn: 0.4357
Epoch 315/550
94/94 [==============================] - 0s 923us/step - loss: 0.0201 - mae: 0.0995 - r2_score_nn: 0.4435 - val_loss: 0.0214 - val_mae: 0.0996 - val_r2_score_nn: 0.4359
Epoch 316/550
94/94 [==============================] - 0s 906us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4230 - val_loss: 0.0214 - val_mae: 0.0996 - val_r2_score_nn: 0.4359
Epoch 317/550
94/94 [==============================] - 0s 895us/step - loss: 0.0201 - mae: 0.0993 - r2_score_nn: 0.4352 - val_loss: 0.0214 - val_mae: 0.0996 - val_r2_score_nn: 0.4360
Epoch 318/550
94/94 [==============================] - 0s 909us/step - loss: 0.0201 - mae: 0.0992 - r2_score_nn: 0.4421 - val_loss: 0.0214 - val_mae: 0.0996 - val_r2_score_nn: 0.4361
Epoch 319/550
94/94 [==============================] - 0s 902us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4446 - val_loss: 0.0214 - val_mae: 0.0996 - val_r2_score_nn: 0.4362
Epoch 320/550
94/94 [==============================] - 0s 910us/step - loss: 0.0201 - mae: 0.0994 - r2_score_nn: 0.4250 - val_loss: 0.0214 - val_mae: 0.0995 - val_r2_score_nn: 0.4363
Epoch 321/550
94/94 [==============================] - 0s 918us/step - loss: 0.0201 - mae: 0.0991 - r2_score_nn: 0.4406 - val_loss: 0.0214 - val_mae: 0.0996 - val_r2_score_nn: 0.4363
Epoch 322/550
94/94 [==============================] - 0s 912us/step - loss: 0.0201 - mae: 0.0992 - r2_score_nn: 0.4385 - val_loss: 0.0214 - val_mae: 0.0995 - val_r2_score_nn: 0.4364
Epoch 323/550
94/94 [==============================] - 0s 904us/step - loss: 0.0201 - mae: 0.0992 - r2_score_nn: 0.4213 - val_loss: 0.0214 - val_mae: 0.0995 - val_r2_score_nn: 0.4365
Epoch 324/550
94/94 [==============================] - 0s 896us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4462 - val_loss: 0.0214 - val_mae: 0.0995 - val_r2_score_nn: 0.4365
Epoch 325/550
94/94 [==============================] - 0s 901us/step - loss: 0.0200 - mae: 0.0993 - r2_score_nn: 0.4471 - val_loss: 0.0214 - val_mae: 0.0995 - val_r2_score_nn: 0.4367
Epoch 326/550
94/94 [==============================] - 0s 905us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4499 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4368
Epoch 327/550
94/94 [==============================] - 0s 907us/step - loss: 0.0200 - mae: 0.0992 - r2_score_nn: 0.4294 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4370
Epoch 328/550
94/94 [==============================] - 0s 917us/step - loss: 0.0200 - mae: 0.0993 - r2_score_nn: 0.4383 - val_loss: 0.0214 - val_mae: 0.0993 - val_r2_score_nn: 0.4371
Epoch 329/550
94/94 [==============================] - 0s 913us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4509 - val_loss: 0.0214 - val_mae: 0.0993 - val_r2_score_nn: 0.4371
Epoch 330/550
94/94 [==============================] - 0s 913us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4505 - val_loss: 0.0214 - val_mae: 0.0993 - val_r2_score_nn: 0.4372
Epoch 331/550
94/94 [==============================] - 0s 904us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4313 - val_loss: 0.0214 - val_mae: 0.0992 - val_r2_score_nn: 0.4374
Epoch 332/550
94/94 [==============================] - 0s 906us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4380 - val_loss: 0.0214 - val_mae: 0.0993 - val_r2_score_nn: 0.4374
Epoch 333/550
94/94 [==============================] - 0s 920us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4411 - val_loss: 0.0214 - val_mae: 0.0993 - val_r2_score_nn: 0.4374
Epoch 334/550
94/94 [==============================] - 0s 888us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4328 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4373
Epoch 335/550
94/94 [==============================] - 0s 910us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4300 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4374
Epoch 336/550
94/94 [==============================] - 0s 910us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4402 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4374
Epoch 337/550
94/94 [==============================] - 0s 862us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4412 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4375
Epoch 338/550
94/94 [==============================] - 0s 918us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4342 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4376
Epoch 339/550
94/94 [==============================] - 0s 904us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4387 - val_loss: 0.0214 - val_mae: 0.0994 - val_r2_score_nn: 0.4377
Epoch 340/550
94/94 [==============================] - 0s 909us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4489 - val_loss: 0.0214 - val_mae: 0.0993 - val_r2_score_nn: 0.4378
Epoch 341/550
94/94 [==============================] - 0s 881us/step - loss: 0.0200 - mae: 0.0992 - r2_score_nn: 0.4292 - val_loss: 0.0213 - val_mae: 0.0992 - val_r2_score_nn: 0.4380
Epoch 342/550
94/94 [==============================] - 0s 883us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4159 - val_loss: 0.0213 - val_mae: 0.0992 - val_r2_score_nn: 0.4381
Epoch 343/550
94/94 [==============================] - 0s 916us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4325 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4382
Epoch 344/550
94/94 [==============================] - 0s 914us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4450 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4383
Epoch 345/550
94/94 [==============================] - 0s 902us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4358 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4384
Epoch 346/550
94/94 [==============================] - 0s 909us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4350 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4385
Epoch 347/550
94/94 [==============================] - 0s 922us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4287 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4385
Epoch 348/550
94/94 [==============================] - 0s 901us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4191 - val_loss: 0.0213 - val_mae: 0.0992 - val_r2_score_nn: 0.4385
Epoch 349/550
94/94 [==============================] - 0s 908us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4222 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4386
Epoch 350/550
94/94 [==============================] - 0s 901us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4476 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4386
Epoch 351/550
94/94 [==============================] - 0s 918us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4437 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4387
Epoch 352/550
94/94 [==============================] - 0s 910us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4354 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4389
Epoch 353/550
94/94 [==============================] - 0s 909us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4438 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4390
Epoch 354/550
94/94 [==============================] - 0s 854us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4546 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4391
Epoch 355/550
94/94 [==============================] - 0s 914us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4387 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4390
Epoch 356/550
94/94 [==============================] - 0s 861us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4533 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4391
Epoch 357/550
94/94 [==============================] - 0s 903us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4377 - val_loss: 0.0213 - val_mae: 0.0991 - val_r2_score_nn: 0.4391
Epoch 358/550
94/94 [==============================] - 0s 881us/step - loss: 0.0200 - mae: 0.0991 - r2_score_nn: 0.4424 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4393
Epoch 359/550
94/94 [==============================] - 0s 928us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4436 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4393
Epoch 360/550
94/94 [==============================] - 0s 971us/step - loss: 0.0200 - mae: 0.0989 - r2_score_nn: 0.4368 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4394
Epoch 361/550
94/94 [==============================] - 0s 990us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4301 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4395
Epoch 362/550
94/94 [==============================] - 0s 906us/step - loss: 0.0200 - mae: 0.0987 - r2_score_nn: 0.4401 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4395
Epoch 363/550
94/94 [==============================] - 0s 912us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4471 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4395
Epoch 364/550
94/94 [==============================] - 0s 876us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4225 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4396
Epoch 365/550
94/94 [==============================] - 0s 1000us/step - loss: 0.0200 - mae: 0.0990 - r2_score_nn: 0.4308 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4398
Epoch 366/550
94/94 [==============================] - 0s 964us/step - loss: 0.0200 - mae: 0.0986 - r2_score_nn: 0.4494 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4397
Epoch 367/550
94/94 [==============================] - 0s 925us/step - loss: 0.0200 - mae: 0.0987 - r2_score_nn: 0.4451 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4398
Epoch 368/550
94/94 [==============================] - 0s 930us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4378 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4398
Epoch 369/550
94/94 [==============================] - 0s 902us/step - loss: 0.0200 - mae: 0.0987 - r2_score_nn: 0.4203 - val_loss: 0.0213 - val_mae: 0.0990 - val_r2_score_nn: 0.4398
Epoch 370/550
94/94 [==============================] - 0s 911us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4319 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4398
Epoch 371/550
94/94 [==============================] - 0s 908us/step - loss: 0.0200 - mae: 0.0987 - r2_score_nn: 0.4318 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4399
Epoch 372/550
94/94 [==============================] - 0s 900us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4306 - val_loss: 0.0213 - val_mae: 0.0989 - val_r2_score_nn: 0.4401
Epoch 373/550
94/94 [==============================] - 0s 915us/step - loss: 0.0200 - mae: 0.0988 - r2_score_nn: 0.4407 - val_loss: 0.0213 - val_mae: 0.0988 - val_r2_score_nn: 0.4401
Epoch 374/550
94/94 [==============================] - 0s 905us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4436 - val_loss: 0.0213 - val_mae: 0.0988 - val_r2_score_nn: 0.4402
Epoch 375/550
94/94 [==============================] - 0s 906us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4414 - val_loss: 0.0213 - val_mae: 0.0988 - val_r2_score_nn: 0.4402
Epoch 376/550
94/94 [==============================] - 0s 902us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4324 - val_loss: 0.0213 - val_mae: 0.0988 - val_r2_score_nn: 0.4402
Epoch 377/550
94/94 [==============================] - 0s 905us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4283 - val_loss: 0.0213 - val_mae: 0.0988 - val_r2_score_nn: 0.4403
Epoch 378/550
94/94 [==============================] - 0s 901us/step - loss: 0.0199 - mae: 0.0988 - r2_score_nn: 0.4240 - val_loss: 0.0213 - val_mae: 0.0988 - val_r2_score_nn: 0.4403
Epoch 379/550
94/94 [==============================] - 0s 911us/step - loss: 0.0199 - mae: 0.0988 - r2_score_nn: 0.4400 - val_loss: 0.0213 - val_mae: 0.0987 - val_r2_score_nn: 0.4405
Epoch 380/550
94/94 [==============================] - 0s 918us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4521 - val_loss: 0.0213 - val_mae: 0.0987 - val_r2_score_nn: 0.4405
Epoch 381/550
94/94 [==============================] - 0s 913us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4428 - val_loss: 0.0213 - val_mae: 0.0987 - val_r2_score_nn: 0.4406
Epoch 382/550
94/94 [==============================] - 0s 905us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4438 - val_loss: 0.0212 - val_mae: 0.0987 - val_r2_score_nn: 0.4407
Epoch 383/550
94/94 [==============================] - 0s 909us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4303 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4409
Epoch 384/550
94/94 [==============================] - 0s 923us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4526 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4409
Epoch 385/550
94/94 [==============================] - 0s 849us/step - loss: 0.0199 - mae: 0.0984 - r2_score_nn: 0.4349 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4408
Epoch 386/550
94/94 [==============================] - 0s 916us/step - loss: 0.0199 - mae: 0.0986 - r2_score_nn: 0.4381 - val_loss: 0.0212 - val_mae: 0.0987 - val_r2_score_nn: 0.4408
Epoch 387/550
94/94 [==============================] - 0s 905us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4335 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4409
Epoch 388/550
94/94 [==============================] - 0s 875us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4297 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4409
Epoch 389/550
94/94 [==============================] - 0s 855us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4352 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4411
Epoch 390/550
94/94 [==============================] - 0s 840us/step - loss: 0.0199 - mae: 0.0986 - r2_score_nn: 0.4203 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4411
Epoch 391/550
94/94 [==============================] - 0s 896us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4291 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4411
Epoch 392/550
94/94 [==============================] - 0s 911us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4364 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4412
Epoch 393/550
94/94 [==============================] - 0s 873us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4231 - val_loss: 0.0212 - val_mae: 0.0987 - val_r2_score_nn: 0.4411
Epoch 394/550
94/94 [==============================] - 0s 873us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4498 - val_loss: 0.0212 - val_mae: 0.0987 - val_r2_score_nn: 0.4412
Epoch 395/550
94/94 [==============================] - 0s 877us/step - loss: 0.0199 - mae: 0.0986 - r2_score_nn: 0.4394 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4413
Epoch 396/550
94/94 [==============================] - 0s 876us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4549 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4414
Epoch 397/550
94/94 [==============================] - 0s 884us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4350 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4414
Epoch 398/550
94/94 [==============================] - 0s 868us/step - loss: 0.0199 - mae: 0.0985 - r2_score_nn: 0.4377 - val_loss: 0.0212 - val_mae: 0.0987 - val_r2_score_nn: 0.4413
Epoch 399/550
94/94 [==============================] - 0s 861us/step - loss: 0.0199 - mae: 0.0987 - r2_score_nn: 0.4400 - val_loss: 0.0212 - val_mae: 0.0986 - val_r2_score_nn: 0.4414
In [249]:
plt.figure(figsize=(14, 4))

plt.subplot(1, 3, 1)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.subplot(1, 3, 2)
plt.plot(history.history['mae'], label='Training MAE')
plt.plot(history.history['val_mae'], label='Validation MAE')
plt.title('Training and Validation MAE')
plt.xlabel('Epoch')
plt.ylabel('MAE')
plt.legend()


plt.subplot(1, 3, 3)
plt.plot(history.history['r2_score_nn'], label='Training R-squared')
plt.plot(history.history['val_r2_score_nn'], label='Validation R-squared')
plt.title('Validation R-squared')
plt.xlabel('Epoch')
plt.ylabel('R-squared')
plt.legend()

plt.tight_layout()
plt.show()
No description has been provided for this image

Residuals are plotted against predicted values to check for patterns that may indicate issues with the model. A histogram of the residuals helps assess their distribution, revealing insights into the model's accuracy.

In [250]:
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(14, 6))

plt.subplot(1, 2, 1)

y_pred = model.predict(X_test)

test_residuals = y_test - y_pred.squeeze()

# 3. Plot residuals to check if there are patterns
plt.scatter(y_pred, test_residuals, alpha=0.5)
plt.hlines(y=0, xmin=np.min(y_pred), xmax=np.max(y_pred), colors='r', linestyles='--')
plt.xlabel('Predicted Values')
plt.ylabel('Residuals')
plt.title('Residuals vs. Predicted Values')

plt.subplot(1, 2, 2)
plt.hist(test_residuals, bins=50, alpha=0.75, color='b')
plt.xlabel('Residuals')
plt.ylabel('Frequency')
plt.title('Distribution of Residuals')
plt.show()
19/19 [==============================] - 0s 611us/step
No description has been provided for this image

Finally, the model is evaluated on the test set to obtain metrics such as MSE, MAE, and the custom R-squared score, providing an assessment of its performance.

In [251]:
score = model.evaluate(X_test, y_test)
print('Test loss (MSE):', score[0])
print('Test MAE:', score[1])
print('Test R-squared:', score[2])
19/19 [==============================] - 0s 510us/step - loss: 0.0158 - mae: 0.0884 - r2_score_nn: 0.5256
Test loss (MSE): 0.015842191874980927
Test MAE: 0.08838861435651779
Test R-squared: 0.5255863666534424

The learning curves for the neural network appear promising, but the residual plots reveal a funnel shape, signaling heteroscedasticity. This suggests that the model may not be fully capturing the complexities of the data. Interestingly, the model's performance, as indicated by the Mean Squared Error (MSE) and R-squared values, is quite comparable to simpler models like Linear Regression and Random Forest. Despite the neural network's deeper architecture, it does not provide substantial improvement over these more basic approaches.

The residual analysis further emphasizes the issue, as the model exhibits the same funnel-shaped heteroscedasticity seen in other models, indicating similar limitations. Even after tweaking the parameters, the residuals remained unchanged, suggesting that significant gains may only come with extensive hyperparameter tuning or more advanced feature engineering.

Weighted least squares regression (WLS)¶

In this section, I applied a weighted least squares (WLS) regression model to the data. WLS accounts for heteroscedasticity by giving more weight to observations with smaller residuals. The model summary shows high R-squared values, indicating a good fit. Notably, area_2018, diff_2018_2019, and growth_2018_2019 were significant predictors.

In [252]:
X=test[['area_2016',
 'area_2018',
 'area_2019',
 'diff_2018_2019',
 'growth_2018_2019',
 ]]
y=test[['area_2021']]

initial_model = sm.OLS(y, X).fit()
residuals = initial_model.resid
fitted_values = initial_model.fittedvalues



weights = 1 / np.abs(residuals)  
weighted_model = sm.WLS(y, X, weights=weights).fit()

print(weighted_model.summary())
                                 WLS Regression Results                                
=======================================================================================
Dep. Variable:              area_2021   R-squared (uncentered):                   0.987
Model:                            WLS   Adj. R-squared (uncentered):              0.987
Method:                 Least Squares   F-statistic:                          4.483e+04
Date:                Wed, 18 Sep 2024   Prob (F-statistic):                        0.00
Time:                        23:53:51   Log-Likelihood:                          3477.8
No. Observations:                2922   AIC:                                     -6946.
Df Residuals:                    2917   BIC:                                     -6916.
Df Model:                           5                                                  
Covariance Type:            nonrobust                                                  
====================================================================================
                       coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------
area_2016           -0.0857      0.025     -3.455      0.001      -0.134      -0.037
area_2018            0.8339      0.031     27.053      0.000       0.773       0.894
area_2019           -0.0073      0.019     -0.376      0.707      -0.045       0.031
diff_2018_2019      -0.1099      0.005    -20.108      0.000      -0.121      -0.099
growth_2018_2019     0.2399      0.006     40.185      0.000       0.228       0.252
==============================================================================
Omnibus:                      307.833   Durbin-Watson:                   2.019
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              405.027
Skew:                          -0.903   Prob(JB):                     1.12e-88
Kurtosis:                       2.744   Cond. No.                         57.7
==============================================================================

Notes:
[1] R² is computed without centering (uncentered) since the model does not contain a constant.
[2] Standard Errors assume that the covariance matrix of the errors is correctly specified.

The residuals vs. fitted values plot shows a funnel-shaped pattern, which is a sign of heteroscedasticity. Even with WLS, the residuals exhibit the same trend, confirming that the model still struggles with this issue.

In [253]:
# Predict the values using the fitted model
fitted_values = weighted_model.fittedvalues
residuals = weighted_model.resid

# Plot Residuals vs. Fitted Values
plt.figure(figsize=(10, 6))
sns.scatterplot(x=fitted_values, y=residuals, alpha=0.5)
plt.axhline(0, color='red', linestyle='--')
plt.xlabel('Fitted Values')
plt.ylabel('Residuals')
plt.title('Residuals vs. Fitted Values')
plt.show()
No description has been provided for this image

The histogram of residuals indicates that the residuals are not perfectly normally distributed. The presence of skewness could suggest model misspecification or the need for more complex transformations.

In [254]:
plt.figure(figsize=(10, 6))
sns.histplot(residuals, kde=True)
plt.xlabel('Residuals')
plt.title('Histogram of Residuals')
plt.show()
No description has been provided for this image

This plot examines the relationship between each feature and the residuals. The funnel shape is particularly evident in area_2016 and area_2018 suggesting that these variables could be contributing to the heteroscedasticity. Based on this, I focused on area_2019, diff_2018_2019 and growth_2018_2019 in the final model.

In [255]:
plt.figure(figsize=(12, 6))
for col in X.columns:
    plt.subplot(2, 4, X.columns.get_loc(col) + 1)
    sns.scatterplot(x=X[col], y=residuals, alpha=0.5)
    plt.axhline(0, color='red', linestyle='--')
    plt.title(f'Residuals vs. {col}')
plt.tight_layout()
plt.show()
No description has been provided for this image

Cook's Distance measures the influence of each observation on the fitted model. Points with a Cook's Distance greater than the threshold are flagged as influential. These points can disproportionately affect the model and should be examined closely for potential removal or further investigation. 1 has been used as a threshold.

In [256]:
from statsmodels.stats.outliers_influence import OLSInfluence

influence = OLSInfluence(weighted_model)
cooks_d = influence.cooks_distance

plt.figure(figsize=(10, 6))
plt.stem(np.arange(len(cooks_d[0])), cooks_d[0], markerfmt=",")
plt.title("Cook's Distance")
plt.xlabel("Index")
plt.ylabel("Cook's Distance")
plt.axhline(1 / len(cooks_d[0]), color='r', linestyle='--')
plt.show()
No description has been provided for this image
In [257]:
influential_indices = np.where(cooks_d[0] > (1 / len(cooks_d[0])))[0]

print("Influential Points (Indices):", influential_indices)

# Visualize influential points
plt.figure(figsize=(10, 6))
plt.scatter(np.arange(len(cooks_d[0])), cooks_d[0], color='blue', label='Cook\'s Distance')
plt.axhline(1 / len(cooks_d[0]), color='red', linestyle='--', label='Threshold')
plt.title("Cook's Distance")
plt.xlabel("Index")
plt.ylabel("Cook's Distance")
plt.legend()
plt.show()
Influential Points (Indices): [   3   30  166  418  441  492  594 1205 1338 1549 1980 2085 2709 2841
 2842]
No description has been provided for this image

Weighted Least Squares and Cook's Distance¶

Weighted Least Squares (WLS) was chosen because it addressed key issues identified in the initial OLS model, particularly heteroscedasticity and the presence of influential points. The residuals plot from the OLS model indicated non-constant variance, violating assumptions necessary for reliable results. By applying WLS, where weights were based on residuals, the residuals vs. fitted values plot improved, showing more uniform variance. Cook’s distance helped identify and remove influential points, which further enhanced the model's fit. Additionally, the WLS model achieved a high R-squared of 0.981, indicating it explained most of the variance in the response variable (area_2021). The diagnostics also showed better residual normality and homoscedasticity, making WLS the more suitable method for this dataset.

The model is set up to predict the tree canopy area for 2021 using the area of 2019, differences and growth between 2018 and 2019. The features area_2019, diff_2018_2019 and growth_2018_2019 are used as predictors, and a constant term is added for the intercept.

Initial OLS Model and Influential Point Detection: An Ordinary Least Squares (OLS) model is fitted to the original data. Cook's distance is calculated to detect influential points, which are removed from the dataset to improve model performance.

Model Refitting After Removing Influential Points: The OLS model is refitted after removing the identified influential points. Residuals are analyzed to ensure model improvement.

Weighted Least Squares (WLS) Model: Weights are calculated based on the residuals of the cleaned OLS model. A Weighted Least Squares (WLS) model is fitted to the cleaned data, improving the model fit by handling heteroscedasticity.

Residuals and fitted values of the WLS model are plotted to visually assess the performance. A histogram of the residuals and a scatter plot of residuals vs fitted values are provided for diagnostic purposes.

In [258]:
X = test[['area_2019', 'diff_2018_2019', 'growth_2018_2019']]
y = test['area_2021']
X = X.reset_index(drop=True)
y = y.reset_index(drop=True)

# Add a constant for intercept
X = sm.add_constant(X)

# Fit initial OLS model
initial_model = sm.OLS(y, X).fit()

# Get Cook's distance
influence = initial_model.get_influence()
cooks_d = influence.cooks_distance[0]

# Identify influential points based on Cook's distance
influential_indices = np.where(cooks_d > (1 / len(cooks_d)))[0]

# Remove influential points
X_cleaned = X.drop(index=influential_indices)
y_cleaned = y.drop(index=influential_indices)

# Refit the model with the cleaned data
cleaned_model = sm.OLS(y_cleaned, X_cleaned).fit()
residuals_cleaned = cleaned_model.resid

weights = 1 / np.abs(residuals_cleaned)

# Fit the weighted least squares model
weighted_model = sm.WLS(y_cleaned, X_cleaned, weights=weights).fit()

# Print summary of the weighted model
print(weighted_model.summary())

# Residual analysis
residuals_weighted = weighted_model.resid
fitted_values_weighted = weighted_model.fittedvalues

# Plot residuals vs fitted values for the weighted model
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.scatter(fitted_values_weighted, residuals_weighted)
plt.axhline(0, color='r', linestyle='--')
plt.title("Residuals vs Fitted Values (Weighted Model)")
plt.xlabel("Fitted Values")
plt.ylabel("Residuals")

plt.subplot(1, 2, 2)
plt.hist(residuals_weighted, bins=30, edgecolor='k')
plt.title("Histogram of Residuals (Weighted Model)")
plt.show()
                            WLS Regression Results                            
==============================================================================
Dep. Variable:              area_2021   R-squared:                       0.984
Model:                            WLS   Adj. R-squared:                  0.984
Method:                 Least Squares   F-statistic:                 4.568e+04
Date:                Wed, 18 Sep 2024   Prob (F-statistic):               0.00
Time:                        23:53:52   Log-Likelihood:                 4664.9
No. Observations:                2281   AIC:                            -9322.
Df Residuals:                    2277   BIC:                            -9299.
Df Model:                           3                                         
Covariance Type:            nonrobust                                         
====================================================================================
                       coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------
const                0.3277      0.009     37.943      0.000       0.311       0.345
area_2019            0.5134      0.015     34.341      0.000       0.484       0.543
diff_2018_2019      -0.5074      0.013    -38.726      0.000      -0.533      -0.482
growth_2018_2019     0.3338      0.008     41.526      0.000       0.318       0.350
==============================================================================
Omnibus:                      249.754   Durbin-Watson:                   1.954
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              231.758
Skew:                          -0.711   Prob(JB):                     4.72e-51
Kurtosis:                       2.353   Cond. No.                         126.
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
No description has been provided for this image

Since the above method looks promising, the dataset is split into training and test sets. The model is fitted to the training data, with influential points removed based on Cook's distance. This section ensures the model's generalizability.

The WLS regression results demonstrate a very strong model fit. With an R-squared value of 0.984, the model explains approximately 98.4% of the variance in the dependent variable, area_2021, indicating an excellent fit to the data. Looking at the individual coefficients, the constant term (0.3277) and the coefficients for area_2019 (0.5134), diff_2018_2019 (-0.5074), and growth_2018_2019 (0.3338) are all highly significant, with p-values of 0.000. This shows strong evidence that these predictors impact area_2021.

The negative coefficient for diff_2018_2019 suggests that larger differences between 2018 and 2019 canopy areas are associated with lower canopy areas in 2021. In contrast, the positive coefficient for growth_2018_2019 indicates that greater growth during the same period is positively associated with the canopy area in 2021. Overall, the model indicates that both the change in area and growth are critical factors influencing the canopy area observed in 2021.

In [259]:
X = test[['area_2019', 'diff_2018_2019', 'growth_2018_2019']]
y = test['area_2021']

# Add a constant for intercept
X = sm.add_constant(X)


# Split the data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=20)
X_train = X_train.reset_index(drop=True)
y_train = y_train.reset_index(drop=True)
X_test = X_test.reset_index(drop=True)
y_test = y_test.reset_index(drop=True)

# Fit initial OLS model on the training data
initial_model_train = sm.OLS(y_train, X_train).fit()

# # Get Cook's distance
influence = initial_model_train.get_influence()
cooks_d = influence.cooks_distance[0]

# Identify influential points based on Cook's distance
influential_indices = np.where(cooks_d > (1/ len(cooks_d)))[0]

# Remove influential points from training data
X_train_cleaned = X_train.drop(index=influential_indices)
y_train_cleaned = y_train.drop(index=influential_indices)

# Refit the model with the cleaned training data
cleaned_model_train = sm.OLS(y_train_cleaned, X_train_cleaned).fit()

# Calculate residuals
residuals_cleaned = cleaned_model_train.resid

weights = 1 / np.abs(residuals_cleaned)

wls_model = sm.WLS(y_train_cleaned, X_train_cleaned, weights=weights).fit()

# Get residuals and fitted values from WLS
residuals_wls = wls_model.resid
fitted_values_wls = wls_model.fittedvalues

# Print summary of the weighted model
print(wls_model.summary())

# Residual analysis for the weighted model
residuals_weighted = wls_model.resid
fitted_values_weighted = wls_model.fittedvalues

# Plot residuals vs fitted values for the weighted model
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.scatter(fitted_values_weighted, residuals_weighted)
plt.axhline(0, color='r', linestyle='--')
plt.title("Residuals vs Fitted Values (Weighted Model)")
plt.xlabel("Fitted Values")
plt.ylabel("Residuals")

plt.subplot(1, 2, 2)
plt.hist(residuals_weighted, bins=30, edgecolor='k')
plt.title("Histogram of Residuals (Weighted Model)")
plt.show()
                            WLS Regression Results                            
==============================================================================
Dep. Variable:              area_2021   R-squared:                       0.984
Model:                            WLS   Adj. R-squared:                  0.984
Method:                 Least Squares   F-statistic:                 3.703e+04
Date:                Wed, 18 Sep 2024   Prob (F-statistic):               0.00
Time:                        23:53:52   Log-Likelihood:                 3779.1
No. Observations:                1829   AIC:                            -7550.
Df Residuals:                    1825   BIC:                            -7528.
Df Model:                           3                                         
Covariance Type:            nonrobust                                         
====================================================================================
                       coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------
const                0.3151      0.010     31.016      0.000       0.295       0.335
area_2019            0.5312      0.017     30.548      0.000       0.497       0.565
diff_2018_2019      -0.4857      0.015    -31.684      0.000      -0.516      -0.456
growth_2018_2019     0.3235      0.009     34.510      0.000       0.305       0.342
==============================================================================
Omnibus:                      203.708   Durbin-Watson:                   1.970
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              169.832
Skew:                          -0.661   Prob(JB):                     1.32e-37
Kurtosis:                       2.307   Cond. No.                         86.5
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
No description has been provided for this image

Diagnostic plots like Q-Q plot and Scale-Location plot are generated to assess the normality and homoscedasticity of residuals. Minor deviations, such as "snaking" patterns, are noted but not deemed critical.

In [260]:
# Q-Q plot for the residuals of the weighted model
sm.qqplot(residuals_weighted, line='s')
plt.title("Q-Q Plot of Residuals (Weighted Model)")
plt.xlabel("Theoretical Quantiles")
plt.ylabel("Sample Quantiles")
plt.show()
No description has been provided for this image

the code calculates standardized residuals from a weighted least squares (WLS) regression model and then creates a Scale-Location plot. The standardized residuals are obtained by dividing the residuals by the square root of the model's scale (variance estimate). The plot displays the fitted values on the x-axis and the standardized residuals on the y-axis, helping to assess the homoscedasticity of the model. The Scale-Location plot shows no significant issues with homoscedasticity, as the standardized residuals are randomly scattered around zero.

In [261]:
standardized_residuals = residuals_weighted / np.sqrt(wls_model.scale)

# Plot Scale-Location Plot
plt.figure(figsize=(10, 6))
sns.scatterplot(x=fitted_values_weighted, y=standardized_residuals, alpha=0.5)
plt.axhline(0, color='red', linestyle='--')
plt.xlabel('Fitted Values')
plt.ylabel('Standardized Residuals')
plt.title('Scale-Location Plot')
plt.show()
No description has been provided for this image

In this analysis, Cook's distance was used to identify influential points in the initial OLS model, which can sometimes skew results and lead to poor model performance. While it's generally not recommended to remove influential points from the test dataset, doing so can be necessary in certain situations where these points significantly affect the model's accuracy and interpretation. By eliminating these outliers, the model can provide a more reliable representation of the underlying relationships in the data, leading to improved performance metrics such as lower MSE and MAE and a higher R-squared value. After removing these influential points, the model was re-evaluated, showing improved performance metrics:

Test MSE: 0.0034, Test MAE: 0.0406 and Test R-squared: 0.8527

The fit of the model improved significantly, indicating a strong correlation between actual and predicted values. The model is now better at predicting outcomes without the distortion caused by the influential points.

In [262]:
initial_model_train = sm.OLS(y_test, X_test).fit()

# Get Cook's distance
influence = initial_model_train.get_influence()
cooks_d = influence.cooks_distance[0]

# Identify influential points based on Cook's distance
influential_indices = np.where(cooks_d > (1/ len(cooks_d)))[0]

# Remove influential points from training data
X_test = X_test.drop(index=influential_indices)
y_test = y_test.drop(index=influential_indices)



y_pred = wls_model.predict(X_test)

# Calculate performance metrics

mse = mean_squared_error(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

print(f"Test MSE: {mse:.4f}")
print(f"Test MAE: {mae:.4f}")
print(f"Test R-squared: {r2:.4f}")

# Plot actual vs predicted values
plt.figure(figsize=(8, 6))
plt.scatter(y_test, y_pred, alpha=0.7)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.title('Actual vs Predicted Values')
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
plt.show()
Test MSE: 0.0034
Test MAE: 0.0406
Test R-squared: 0.8527
No description has been provided for this image

Predicting area changes: inverse transformations for area_2023 based on WLS regression model¶

In this analysis, the model was used to predict area_2023 based on the features area_2021, growth_2019_2021, and diff_2019_2021, leveraging the strong performance of the previous WLS regression model. To make predictions, the new data (X_new) was prepared by adding a constant and using the fitted model to generate predictions (y_pred_new).

In [263]:
X_new = test[['area_2021', 'diff_2019_2021', 'growth_2019_2021']]
X_new = sm.add_constant(X_new)
y_pred_new = wls_model.predict(X_new)

Since the original model's target variable was area_2021, inverse transformations were performed to convert the predicted values back to their original scale. This involved:

Min-Max Scaling: Using the minimum and maximum values from the MinMaxScaler to revert the scaling for y_pred_new.

Standard Scaling: Applying the mean and standard deviation from the StandardScaler to revert the standardized values.

Inverse Transformation: Finally, the transformed values were converted back to the original scale using the appropriate power transformation techniques.

This approach ensures that the predictions for area_2023 are in the same scale and context as the original dataset, allowing for meaningful interpretation of the results.

In [264]:
min_val = minmax_scaler.data_min_[3]  # Get min from MinMaxScaler
max_val = minmax_scaler.data_max_[3]  # Get max from MinMaxScaler
mean_val = standard_scaler.mean_[3]  # Get mean from StandardScaler
std_val = standard_scaler.scale_[3]  # Get std from StandardScaler
In [265]:
y_pred_array = y_pred_new.to_numpy().reshape(-1, 1)
y_pred_2 = (y_pred_array* (max_val-min_val))+min_val

# Inverse Standard Scaling for y_pred
y_pred_1=(y_pred_2*std_val)+mean_val
y_pred_2023 = pt2.inverse_transform(y_pred_1)
/opt/anaconda3/envs/MelbourneCityOpenData/lib/python3.8/site-packages/sklearn/base.py:464: UserWarning: X does not have valid feature names, but PowerTransformer was fitted with feature names
  warnings.warn(

Mapping decreased tree canopy areas from predicted 2023 results¶

A new dataset was created by copying the original GeoDataFrame and adding the predictions for area_2023. The dataset was then filtered to select rows where area_2023 is less than area_2021. This analysis aims to identify areas that have experienced a decrease in tree canopy, allowing for further examination of tree planting schedules and zones in the City of Melbourne to address canopy loss.

In [269]:
new=gdf.copy()
new['area_2023']=y_pred_2023
In [270]:
new=new.loc[new['area_2023']<new['area_2021']]
new = new.reset_index(drop=True)

The GeoDataFrame containing predictions for area_2023 was enhanced by geocoding the coordinates to obtain location names, latitudes, and longitudes using the Nominatim API. A new column for location names was added to the dataset.

In [271]:
geolocator = Nominatim(user_agent="mygeopy")
loc=[]
latitude=[]
longitude=[]
for i in new['geo_point_2021']:
    lat,lon=i.split(", ")
    latitude.append(float(lat))
    longitude.append(float(lon))
    location = geolocator.reverse((float(lat), float(lon)), language='en',timeout=10)
    address=", ".join(location.address.split(", ")[:-4])
    loc.append(address)
In [272]:
new['location']=loc
new['latitude']=latitude
new['longitude']=longitude
new.head(3)
Out[272]:
geo_point_2018 geo_shape_2018 id_2018 area_2018 geo_point_2019 geo_shape_2019 id_2019 area_2019 geo_point_2021 geo_shape_2021 area_2021 geometry geo_point_2016 geo_shape_2016 area_2016 area_2023 location latitude longitude
0 -37.84124695622991, 144.98327151968286 {"coordinates": [[[[144.98331939330936, -37.84... 1213 247.062061 -37.84124271837534, 144.98327716981257 {"coordinates": [[[144.983303002, -37.84133850... 111520 209.046848 -37.841239532163335, 144.98327502632867 {"coordinates": [[[[144.983297929646, -37.8411... 200.507184 POLYGON ((322554.69642 5809885.13204, 322554.5... -37.84124695528669, 144.9832715195055 {"coordinates": [[[144.98331939313212, -37.841... 247.062061 158.041783 Pasley Street North, South Yarra, Melbourne -37.841240 144.983275
1 -37.84093678707895, 144.98142629806895 {"coordinates": [[[[144.9814539423438, -37.840... 1245 339.505443 -37.84093191163414, 144.98143612962346 {"coordinates": [[[144.98132678650003, -37.840... 111414 337.429686 -37.84093140991823, 144.9814366640353 {"coordinates": [[[[144.98139525021838, -37.84... 329.473824 POLYGON ((322374.44642 5809907.63203, 322374.3... -37.840936786135785, 144.98142629789027 {"coordinates": [[[144.98145394216525, -37.840... 339.505443 209.054604 Armadale Street, Melbourne -37.840931 144.981437
2 -37.83034746935204, 144.90367402224626 {"coordinates": [[[[144.90366091680028, -37.83... 5684 133.154440 -37.830348694685895, 144.9036853294421 {"coordinates": [[[144.90372053240003, -37.830... 103188 129.249992 -37.83035301921699, 144.9036790627309 {"coordinates": [[[[144.9036454464987, -37.830... 124.071432 MULTIPOLYGON (((315514.40840 5810943.10200, 31... -37.83034746840935, 144.90367402200675 {"coordinates": [[[144.9036609165608, -37.8302... 133.154440 122.075734 Boeing Aerostructures Australia, 224-260, Lori... -37.830353 144.903679

A folium map was created to visualize tree canopy data from 2018, 2019, and 2021, with layers colored blue, green, and red, respectively. The map is centered around the average latitude and longitude of selected areas and includes markers indicating locations with predicted decreased tree canopy in 2023. This visual representation aids in identifying areas that may need attention for tree planting and restoration efforts in the City of Melbourne.

In [273]:
e = folium.Map(location=[new['latitude'].mean(),new['longitude'].mean()], zoom_start=12)  # Adjust latitude and longitude to your area

# Function to add GeoDataFrames to the map
def add_geo_to_map(gdf, color, year, id):
    geo_json = folium.GeoJson(
        gdf,
        name=year,
        style_function=lambda feature: {
            'fillColor': color,
            'color': color,
            'weight': 1,
            'fillOpacity': 0.5,
        },
        popup=folium.GeoJsonPopup(fields=[id], aliases=[f'{year} ID:']),
        # Tooltip shows ID when hovering over the shape
        tooltip=folium.GeoJsonTooltip(fields=[id], aliases=[f'{year} ID:'])
    ).add_to(e)



# Add each GeoDataFrame to the map with different colors
add_geo_to_map(gdf_2018, 'blue', '2018', 'objectid')
add_geo_to_map(gdf_2019, 'green', '2019', 'id')
add_geo_to_map(gdf_2021, 'red', '2021', 'geo_point_2d')

# Add a layer control to toggle the layers on/off
folium.LayerControl().add_to(e)


for index, row in new.iterrows():

    folium.Marker([row['latitude'], row['longitude']], popup=row['location'],icon=folium.Icon(icon='tree', prefix='fa', color='green'),icon_size=(3, 3)).add_to(e)

    
legend_html = """
        <div style="position: fixed; 
                    bottom: 50px; left: 50px; width: 150px; height: 90px; 
                    border:2px solid grey; z-index:9999; font-size:14px;
                    background-color:white; opacity: 0.8;">
              <p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
              <p style="margin: 0;">Area 2018: <span style="color:blue">&#9679;</span></p>
              <p style="margin: 0;">Area 2019: <span style="color:green">&#9679;</span></p>
              <p style="margin: 0;">Area 2021: <span style="color:red">&#9679;</span></p>
        </div>
        """
        
e.get_root().html.add_child(folium.Element(legend_html))
        
title_html = """
    <h3 style="text-align: center; margin: 10px 0;">Areas with predicted decrease in tree canopy coverage in 2023</h3>
        """
e.get_root().html.add_child(folium.Element(title_html))



e.save("Areas_with_predicted_decrease_in_tree_canopy_coverage_in_2023.html")

Link to the above map: Areas with predicted decrease in tree canopy coverage in 2023

Analyzing tree planting schedules: zones dataset integration¶

In [274]:
zones.head()
Out[274]:
geo_point_2d geo_shape str_from segpart statusid segid streetname streetid schedule mapstatus str_to segdescr
0 -37.8030612625994, 144.96879535330316 {"coordinates": [[[[144.969387175266, -37.8029... NaN NaN NaN 21556 NaN NaN Not determined by precinct plan NaN NaN Pelham Street between Rathdowne Street and Dru...
1 -37.81979235869452, 144.9675112730786 {"coordinates": [[[[144.96828098035, -37.81969... NaN NaN NaN 22067 NaN NaN Years 8 - 10 NaN NaN Riverside Avenue between St Kilda Road and Sou...
2 -37.796602429685905, 144.96990674715127 {"coordinates": [[[[144.969341164027, -37.7965... NaN NaN NaN 20697 NaN NaN Years 5 - 7 NaN NaN Little Palmerston Street between Rathdowne Str...
3 -37.79774465832566, 144.9502620746376 {"coordinates": [[[[144.950283591209, -37.7975... NaN NaN NaN 21195 NaN NaN Complete NaN NaN Chapman Street between Errol Street and Harker...
4 -37.81652483951976, 144.9864951838192 {"coordinates": [[[[144.986101797552, -37.8163... NaN NaN NaN 21945 NaN NaN Not determined by precinct plan NaN NaN Wellington Parade between Simpson Street and P...

The zones dataset is inspected for its structure and missing values, and columns with null values are dropped. The value counts of the 'schedule' column are analyzed to understand the distribution of planting schedules.

In [275]:
zones.shape
Out[275]:
(839, 12)
In [276]:
zones.isna().sum()
Out[276]:
geo_point_2d      0
geo_shape         0
str_from        839
segpart         839
statusid        839
segid             0
streetname      839
streetid        839
schedule          0
mapstatus       839
str_to          839
segdescr          0
dtype: int64
In [277]:
zones.dropna(axis=1,inplace=True)
In [278]:
zones['schedule'].value_counts()
Out[278]:
schedule
Years 5 - 7                        223
Years 8 - 10                       219
Complete                           200
Not determined by precinct plan    177
Years 1 - 4                         20
Name: count, dtype: int64

Using the Nominatim API, the latitude and longitude of each zone are extracted from the 'geo_point_2d' column. The reverse geocoding process generates human-readable location names, which are added to the zones dataset.

In [279]:
geolocator = Nominatim(user_agent="mygeopy")
j=0
latitude=[]
longitude=[]
loc=[]
for i in zones['geo_point_2d']:
    lat,lon=i.split(", ")
    location = geolocator.reverse((float(lat), float(lon)), language='en',timeout=10)
    latitude.append(float(lat))
    longitude.append(float(lon))
    address=", ".join(location.address.split(", ")[:-4])
    loc.append(address)
In [280]:
zones['latitude']=latitude
zones['longitude']=longitude
zones['location']=loc

Geometry objects are created for the 2021 canopy areas in the new dataset and the zones dataset, using the geo_shape column to facilitate spatial analysis.

In [281]:
new['geometry_2021'] = new['geo_shape_2021'].apply(lambda x: shape(eval(x)))
new.head(3)
Out[281]:
geo_point_2018 geo_shape_2018 id_2018 area_2018 geo_point_2019 geo_shape_2019 id_2019 area_2019 geo_point_2021 geo_shape_2021 area_2021 geometry geo_point_2016 geo_shape_2016 area_2016 area_2023 location latitude longitude geometry_2021
0 -37.84124695622991, 144.98327151968286 {"coordinates": [[[[144.98331939330936, -37.84... 1213 247.062061 -37.84124271837534, 144.98327716981257 {"coordinates": [[[144.983303002, -37.84133850... 111520 209.046848 -37.841239532163335, 144.98327502632867 {"coordinates": [[[[144.983297929646, -37.8411... 200.507184 POLYGON ((322554.69642 5809885.13204, 322554.5... -37.84124695528669, 144.9832715195055 {"coordinates": [[[144.98331939313212, -37.841... 247.062061 158.041783 Pasley Street North, South Yarra, Melbourne -37.841240 144.983275 MULTIPOLYGON (((144.983297929646 -37.841167901...
1 -37.84093678707895, 144.98142629806895 {"coordinates": [[[[144.9814539423438, -37.840... 1245 339.505443 -37.84093191163414, 144.98143612962346 {"coordinates": [[[144.98132678650003, -37.840... 111414 337.429686 -37.84093140991823, 144.9814366640353 {"coordinates": [[[[144.98139525021838, -37.84... 329.473824 POLYGON ((322374.44642 5809907.63203, 322374.3... -37.840936786135785, 144.98142629789027 {"coordinates": [[[144.98145394216525, -37.840... 339.505443 209.054604 Armadale Street, Melbourne -37.840931 144.981437 MULTIPOLYGON (((144.9813952502184 -37.84083249...
2 -37.83034746935204, 144.90367402224626 {"coordinates": [[[[144.90366091680028, -37.83... 5684 133.154440 -37.830348694685895, 144.9036853294421 {"coordinates": [[[144.90372053240003, -37.830... 103188 129.249992 -37.83035301921699, 144.9036790627309 {"coordinates": [[[[144.9036454464987, -37.830... 124.071432 MULTIPOLYGON (((315514.40840 5810943.10200, 31... -37.83034746840935, 144.90367402200675 {"coordinates": [[[144.9036609165608, -37.8302... 133.154440 122.075734 Boeing Aerostructures Australia, 224-260, Lori... -37.830353 144.903679 MULTIPOLYGON (((144.9036454464987 -37.83028169...

The zones dataset is filtered based on the planting schedule into categories: short (Years 1-4), medium (Years 5-7), long (Years 8-10), complete, and not determined. Each filtered subset is converted to a GeoDataFrame with a defined coordinate reference system (CRS) of EPSG:4326.

In [282]:
zones['geometry'] = zones['geo_shape'].apply(lambda x: shape(eval(x)))
short=zones.loc[zones['schedule']=='Years 1 - 4']
medium=zones.loc[zones['schedule']=='Years 5 - 7']
long=zones.loc[zones['schedule']=='Years 8 - 10']
complete=zones.loc[zones['schedule']=='Complete']
nd=zones.loc[zones['schedule']=='Not determined by precinct plan']
short = gpd.GeoDataFrame(short, geometry='geometry')
short.crs='EPSG:4326'
medium = gpd.GeoDataFrame(medium, geometry='geometry')
medium.crs='EPSG:4326'
long = gpd.GeoDataFrame(long, geometry='geometry')
long.crs='EPSG:4326'
complete = gpd.GeoDataFrame(complete, geometry='geometry')
complete.crs='EPSG:4326'
nd = gpd.GeoDataFrame(nd, geometry='geometry')
nd.crs='EPSG:4326'

A folium map is generated, centered around the average latitude and longitude from the new dataset, providing a visual representation of the planting zones. The add_geo_to_map function is defined to add each GeoDataFrame to the map with distinct colors representing different planting schedules.Each GeoDataFrame is added to the map with specific colors: yellow for short-term, orange for medium-term, dark red for long-term, green for complete, and purple for not determined. A layer control feature allows toggling between the different planting schedules on the map. Markers are added to represent locations in the new dataset, indicating areas with decreased tree canopy, enhancing the map's informative value.

In [283]:
e = folium.Map(location=[new['latitude'].mean(),new['longitude'].mean()], zoom_start=12)  # Adjust latitude and longitude to your area

def add_geo_to_map(gdf, color, n,id):
    geo_json = folium.GeoJson(
        gdf,
        name=n,
        style_function=lambda feature: {
            'fillColor': color,
            'color': color,
            'weight': 1,
            'fillOpacity': 0.5,
        },
        popup=folium.GeoJsonPopup(fields=[id]),
        # Tooltip shows ID when hovering over the shape
        tooltip=folium.GeoJsonTooltip(fields=[id])
    ).add_to(e)



# Add each GeoDataFrame to the map with different colors
add_geo_to_map(short, 'yellow', 'short', 'location')
add_geo_to_map(medium, 'orange','medium', 'location')
add_geo_to_map(long, 'darkred', 'long','location')
add_geo_to_map(complete, 'green','complete', 'location')
add_geo_to_map(nd, 'purple', 'not determined','location')

# Add a layer control to toggle the layers on/off
folium.LayerControl().add_to(e)

for index, row in new.iterrows():

    folium.Marker([row['latitude'], row['longitude']], popup=row['location'],icon=folium.Icon(icon='tree', prefix='fa', color='green'),icon_size=(3, 3)).add_to(e)

    
legend_html = """
        <div style="position: fixed; 
                    bottom: 50px; left: 50px; width: 150px; height: 130px; 
                    border:2px solid grey; z-index:9999; font-size:14px;
                    background-color:white; opacity: 0.8;">
              <p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
              <p style="margin: 0;">Short: <span style="color:yellow">&#9679;</span></p>
              <p style="margin: 0;">Medium: <span style="color:orange">&#9679;</span></p>
              <p style="margin: 0;">Long: <span style="color:darkred">&#9679;</span></p>
              <p style="margin: 0;">Complete: <span style="color:green">&#9679;</span></p>
              <p style="margin: 0;">Not determined: <span style="color:purple">&#9679;</span></p>
        </div>
        """
        
e.get_root().html.add_child(folium.Element(legend_html))
        
title_html = """
    <h3 style="text-align: center; margin: 10px 0;">Tree planting zones and schedules</h3>
        """
e.get_root().html.add_child(folium.Element(title_html))



# e.save("map3.html")
e
Out[283]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Analysis of tree canopy loss areas and replanting schedules¶

The code iterates through the new dataset to classify areas based on their intersection with planting schedules from the zones dataset. A dictionary (schedule_dict) is used to store locations according to their respective schedules. Areas of 2021 that do not intersect with any planting schedule are tracked in the untouched_ind list. Also records with 'Not determined by precint plan' are also included in untouched_ind list.

In [284]:
schedule_dict={'Years 5 - 7':[],'Years 8 - 10':[],'Years 1 - 4':[],'Complete':[],'Not determined by precinct plan':[]}
untouched_ind=[]

for i,row1 in new.iterrows():
    f=False
    geom1=row1['geometry_2021']
    for j,row2 in zones.iterrows():
        geom2=row2['geometry']

        if geom1.intersects(geom2):
            f=True
            key=row2['schedule']
            schedule_dict[key].append(row2['location'])
            if key=='Not determined by precinct plan':
                untouched_ind.append(i)
            
    if f==False:
        untouched_ind.append(i)
        
        
In [285]:
untouched=new.iloc[untouched_ind,]

A count of the number of locations under each planting schedule is compiled, and a bar chart is created to visualize these counts. The bar chart distinguishes between the scheduled areas and those overlooked for replanting, using specific colors for each schedule type.

The findings reveal a concerning result: nearly 500 locations have been overlooked for tree planting, despite experiencing a decrease in tree canopy. Among the scheduled areas, only approximately 60 have completed tree replanting, while around 5 locations have plans for replanting in the immediate 1 to 4-year timeframe. Additionally, 70 areas have replanting schedules planned for the next 5 to 7 years, and 109 areas are slated for replanting in the subsequent 8 to 10 years. These insights underscore the urgent need for action in addressing the overlooked areas to restore tree canopy cover effectively. The data indicates that while some progress is being made, a significant number of locations remain unaddressed, highlighting the importance of prioritizing replanting efforts in areas with declining tree canopy.

In [286]:
schedule_counts={}
for key,val in schedule_dict.items():
    schedule_counts[key]=len(val)

plt.figure(figsize=(10, 6))
plt.bar(schedule_counts.keys(), schedule_counts.values(), color=['orange', 'red', 'yellow', 'green', 'purple'])
plt.bar('Overlooked', len(untouched), color='lightblue')

plt.xlabel('Schedule type')
plt.ylabel('Number of locations')
plt.title('Tree canopy loss areas scheduled for replanting by the City of Melbourne')
plt.xticks(rotation=90)

plt.show()
No description has been provided for this image

The untouched DataFrame is created from the new dataset, highlighting areas that lack scheduled replanting in 2021. A folium map is generated, centered on the average latitude and longitude of the untouched areas, allowing for visual representation. The add_geo_to_map function is used to add the untouched areas as a red GeoJSON layer to the folium map, indicating where no replanting is planned. Markers are added to the folium map for each location in the untouched DataFrame, visually indicating areas that need attention for tree canopy restoration.

In [287]:
untouched_2021 = gdf_2021.loc[gdf_2021['geo_point_2d'].isin(untouched['geo_point_2021'])]
In [288]:
e = folium.Map(location=[untouched['latitude'].mean(),untouched['longitude'].mean()], zoom_start=12) 

# Function to add GeoDataFrames to the map
def add_geo_to_map(gdf, color, year, id):
    geo_json = folium.GeoJson(
        gdf,
        name=year,
        style_function=lambda feature: {
            'fillColor': color,
            'color': color,
            'weight': 1,
            'fillOpacity': 0.5,
        },
        popup=folium.GeoJsonPopup(fields=[id], aliases=[f'{year} ID:']),
        # Tooltip shows ID when hovering over the shape
        tooltip=folium.GeoJsonTooltip(fields=[id], aliases=[f'{year} ID:'])
    ).add_to(e)


# Add each GeoDataFrame to the map with different colors

add_geo_to_map(untouched_2021, 'red', '2021', 'geo_point_2d')

for index, row in untouched.iterrows():

    folium.Marker([row['latitude'], row['longitude']], popup=row['location'],icon=folium.Icon(icon='tree', prefix='fa', color='green'),icon_size=(3, 3)).add_to(e)

    
legend_html = """
        <div style="position: fixed; 
                    bottom: 50px; left: 50px; width: 150px; height: 30; 
                    border:2px solid grey; z-index:9999; font-size:14px;
                    background-color:white; opacity: 0.8;">
              <p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
              <p style="margin: 0;">Area 2021: <span style="color:red">&#9679;</span></p>

        </div>
        """
        
e.get_root().html.add_child(folium.Element(legend_html))
        
title_html = """
    <h3 style="text-align: center; margin: 10px 0;">Areas in 2021 which lack replanting scheduling plans</h3>
        """
e.get_root().html.add_child(folium.Element(title_html))
# e.save("map4.html")
e
Out[288]:
Make this Notebook Trusted to load map: File -> Trust Notebook

A dictionary (locations_dict) is constructed to count occurrences of locations in the untouched areas. A horizontal bar plot is generated to display the top 10 locations with the most tree canopy patches, providing insights into where replanting efforts may be most needed.

In [289]:
locations_dict={}
for loc in untouched['location']:

    if loc not in locations_dict:
        locations_dict[loc]=1
    else:
        locations_dict[loc]+=1

        

The horizontal bar plot displays the top 15 locations with the highest number of untouched tree planting schedules, highlighting areas that require immediate attention for replanting efforts. Notable findings include Royal Parade, Parkville, which has the highest count with 13 decreasing canopy patches, followed by Royal Parade, Carlton North with 9. Other significant locations are St Kilda Road (8 patches), Elliott Avenue, Parkville (8 patches), and Royal Park Golf Course (7 patches).

These results underscore the urgency for urban tree canopy restoration in these regions, as they show a significant decline in canopy coverage. The visualization serves to identify priority areas for action, emphasizing the need for focused replanting initiatives to mitigate canopy loss in Melbourne.

In [290]:
top_entries = dict(sorted(locations_dict.items(), key=lambda item: item[1], reverse=True)[:15])
plt.figure(figsize=(12, 8))
plt.barh(list(top_entries.keys()), list(top_entries.values()), color='skyblue')

plt.xlabel('Number of decreasing canopy patches')
plt.ylabel('Locations')
plt.title('Top 15 locations with the most overlooked tree planting schedules')
plt.xticks(rotation=0)
plt.xticks(range(0, 19))
plt.gca().invert_yaxis()  # To display the highest value at the top
# Show the plot
plt.show()
No description has been provided for this image

The analysis revealed that the Weighted Least Squares method with Cook's distance for removing influential points was the most effective, achieving a Test MSE of 0.0034, Test MAE of 0.0406, and a Test R-squared of 0.8527 when predicting 2021 tree canopy loss. Using this model to predict 2023 canopy loss based on 2019 and 2021 data, it was found that nearly 500 locations experiencing canopy decline were overlooked for replanting. Of the scheduled areas, only 60 have completed replanting, with 5 scheduled for the next 1-4 years, 70 for 5-7 years, and 109 for 8-10 years. The results highlight key areas such as Royal Parade in Parkville, The University of Melbourne in Parkville, Royal Parade in Carlton North, St Kilda Road, Elliott Avenue and Royal Park Golf Course in The Avenue where immediate replanting efforts are needed to address canopy loss and restore urban greenery. This emphasizes the need for more focused replanting initiatives in Melbourne.

References:

SGD indicators